引用计数器
每个OC对象都有自己的引用计数器,是一个整数表示对象被引用的次数,即现在有多少东西在使用这个对象。对象刚被创建时,默认计数器值为1,当计数器的值变为0时,则对象销毁。
在每个OC对象内部,都专门有4个字节的存储空间来存储引用计数器。
作用:
判断对象要不要回收的唯一依据就是计数器是否为0,若不为0则存在。
操作:
给对象发送消息,进行相应的计数器操作。
- Retain消息:使计数器+1,该方法返回对象本身
- Release消息:使计数器-1(并不代表释放对象)
- retainCount消息:获得对象当前的引用计数器值
对象的销毁:
当一个对象的引用计数器为0时,那么它将被销毁,其占用的内存被系统回收。
当对象被销毁时,系统会自动向对象发送一条dealloc消息,一般会重写dealloc方法,在这里释放相关的资源,dealloc就像是对象的“临终遗言”。一旦重写了dealloc方法就必须调用[super dealloc],并且放在代码块的最后调用(不能直接调用dealloc方法)。
Person.m:
#import <Person.h> @implementation Person - (void) dealloc{ NSLog(@"Person dealloc..."); [super dealloc]; } @end
main.m
#import <Person.h> int main(){ // 当我们调用alloc之后,p的引用计数器+1 Person *p = [[Person alloc] init]; // 手动调用release方法来对计数器-1,当检测到计数器为0时,就会调用dealloc方法销毁对象 [p release]; return 0; }
相关概念
野指针:
指针指向一个已经被回收的地址。
如上例中,在指向完[p release]之后,p就作为一个野指针存在。
空指针:
没有指向任何东西的指针(存储的东西是0,null,nil),给空指针发送消息不会报错。
如上例中,我们将[p release]改成 p = nil, p即为空指针。
原则
1、准则:
- 只要还有人在使用某个对象,那么这个对象就不会被回收
- 只要你想使用这个对象,那么就应该让这个对象的引用计数器+1
- 当你不想使用这个对象时,应该让对象的引用计数器-1
2、谁创建,谁release
如果你通过alloc,new,copy来创建了一个对象,那么你就必须调用release或者autorelease方法。
3、谁retain,谁release
只要你调用了retain,无论这个对象时如何生成的,你都要调用release
4、总结
有始有终,有加就应该有减。曾经让某个对象计数器加1,就应该让其在最后-1。
代码规范
1、只要调用了alloc,就必须有release(autorelease)
2、Set方法的代码规范
(1) 基本数据类型
- (void) setAge: (int) age{ _age = age; }
(2) OC对象类型
- (void) setCar: (Car *) car{ // 判断新设置的对象是否是成员变量里保存的对象 if(car != _car){ // 对旧对象进行release [_car release]; // 对新对象进行retain并赋值给成员变量(retain方法返回对象本身) _car = [car retain]; } }
3、dealloc方法
- 一定要[super dealloc],而且要放到最后
- 对self(当前)所拥有的的其他对象做一次release操作
Autorelease
Person *p = [[[Person alloc] init] autorelease];
基本:
- 会将对象放到一个自动释放池中
- 当自动释放池被销毁时,会对池子里的所有对象做一次release
- 会返回对象本身
- 调用完autorelease方法后,对象的计数器不受影响(销毁时影响)
好处:
- 不需要再关心对象释放的时间
- 不需要再关心什么时候调用release
注意:
- 连续调用多次autorelease,释放池销毁时执行两次release
Person *p = [[[[Person alloc] init] autorelease] autorelease];
- alloc之后调用了autorelease,之后又调用了release。
Person *p = [[[Person alloc] init] autorelease]; [p release];
自动释放池:
- 在ios程序运行过程中,会创建无数个池子,这些池子都是以栈结构(先进后出)存在的
- 当一个对象调用autorelease时,会将这个对象放到位于栈顶的释放池中
自动释放池的创建方式:
- iOS 5.0以前的创建方式
NSAutoreleasePool *pool=[[NSAutoreleasePool alloc] init]; // ...逻辑代码 [pool release]; // 在mac中,使用[pool drain]
- iOS 5.0以后
@autoreleasepool{ // ...逻辑代码 }
ARC内存管理机制
ARC是Automatic Reference Counting---自动引用计数。
有自动引用计数,那么就得有手动引用计数MRC(Mannul Reference Counting),前面已经提到的就是MRC。
指针类型:
指针分为强指针和弱指针
// 强指针 Person *p = [[[Person alloc] init] autorelease]; // 强指针 __strong Person *p = [[[Person alloc] init] autorelease]; // 弱指针 __weak Person *p = [[[Person alloc] init] autorelease];
也就是说,通过__strong和__week来区分强指针和弱指针,默认情况下,所有指针都是强指针,前面有个__strong
特点:
- 不允许调用release,retain,retainCount等内存管理方法
- 允许重写dealloc, 但是不允许调用[super dealloc]
- 当没有强指针指向这个对象时,这个对象就会被回收