ARC
ARC(Automatic Reference Counting),自动引用计数器
是指内存管理中对引用采取 自动计数的技术。
在OC中,采用ARC机制,让编译器来进行内存管理。
- 自己生成的对象,自己所持有
- 非自己生成的对象,自己也能持有
- 不再需要自己持有的对象,释放
- 非自己持有的对象无法释放
OC内存管理中的alloc/retain/release/dealloc 方法分别指代:
NSObject 类的alloc 类方法、retain 实例方法、release 实例方法和 dealloc 实例方法。
autorelease提供这样的功能:使对象在超出指定的生存范围时,能够自动并正确地释放(调用release方法)
release与autorelease的区别
- 当引用计数器等于0时,调用dealloc实例方法
autorelease
autorelease就是自动释放池
autorelease的具体使用方法如下:
- 生成并持有NSAutoreleasePool对象
- 调用已分配对象的autorelease实例方法
- 废弃NSAutoreleasePool对象
NSAutoreleasePool对象的生命周期:
问:MRC中有自动释放池吗?MRC中有autorelease吗?
有
MRC有自动释放池,有autorelease
ARC只是在编译阶段,遇到需要添加autorelease的地方,自动添加上,不需要手动添加而已
问:在MRC中,调用如下代码会如何:
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
[pool autorelease];
Crash
在ARC中,id类型和对象类型同C语言其他类型不同,其类型上必须附加所有权修饰符。
也就是,对象类型前面,必须有所有权修饰符
所有权修饰符有4种
__strong修饰符
__weak修饰符
__unsafe_unretained修饰符
__strong修饰符
__strong修饰符是对象的默认修饰符
id obj = [[NSObject alloc] init]
其实是:
id __strong obj = [[NSObject alloc] init]
__strong、__weak、__autoreleasing都可以保证将附有这些修饰符的自动变量初始化为nil
id __strong obj0;
id __weak obj1;
id __autoreleaing obj2;
与
id __strong obj0 = nil;
id __weak obj1 = nil;
id __autoreleaing obj2 = nil;
相同
__weak修饰符
__weak修饰符避免了循环引用问题,并且可以在该对象被废弃的时候,弱引用将自动失效且处于nil被赋值的状态
__unsafe_unretained修饰符
ARC的内存管理是编译器的工作,但附有__unsafe_unretained修饰符的变量不属于编译器的内存管理对象。
附有__unsafe_unretained修饰符的变量,同附有__weak修饰符的变量一样,因为自己生成并持有的对象不能继续为自己所有,所以生成的对象会立即被释放。
与__weak不同的是,在对象被废弃的时候,不会主动被赋值为nil,再访问容易造成悬垂指针。
__autoreleasing修饰符
在ARC中,不允许使用autorelease方法,也不能使用NSAutoreleasePool类。
虽然程序员无法直接使用,但实际上,ARC的autorelease功能是起作用的
MRC下:
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
id obj = [[NSObject alloc] init];
[obj autorelease];
[pool drain];
ARC下:
@autoreleasepool {
id _autoreleaseing obj = [[NSObject alloc] init];
}
使用 ”@autoreleasepool块“ 来替代 ”NSAutoreleasePool类对象生成、持有以及废弃“ 这一范围。
ARC下,通过将对象赋值给附加了__autoreleasing修饰符的变量来替代调用autorelease方法(MRC下的[obj autorelease]
)。这一就完成了对象被注册到autoreleasepool
取得非自己生成并持有的对象时,ARC下,编译器会检查方法名是否以alloc/new/copy/mutableCopy开始,如果不是,则自动将返回值的对象注册到autoreleasepool
举个例子
id __strong obj = [NSMutableArray array];
编译器判断其方法名后,自动注册到autoreleasepool
@autoreleasepool {
id __strong obj = [NSMutableArray array];
}
init方法返回值的对象不注册到autoreleasepool
使用ARC需要遵守的规则
ARC编译代码时,需要遵守的规则:
- 不能使用 retain/release/retainCount/autorelease
- 不能使用 NSAllocateObject/NSDeallocateObject
- 须遵守内存管理的方法命名规则
- 不要显式调用 dealloc
- 使用 @autoreleasepool 块替代 NSAutoreleasePool
- 不能使用区域 (NSZone)
- 对象型变量不能作为 C 语言结构体 (struct/union)的成员
- 显式转换“id”和“void *”
在ARC下,以init方法创建的对象,不注册到autoreleasepool上。
init方法的作用,基本上只是对alloc方法返回值的对象进行初始化处理,并返回该对象
对象被废弃时,不管ARC还是MRC,都会调用对象的dealloc方法
C语言的结构体成员中,如果存在OC对象型变量,便会引起编译错误。
属性
ARC的实现
ARC由LLVM编译器和运行时来实现
{
id __strong obj = [[NSObject alloc] init];
}
编译后的模拟代码:
id obj = objc_msgSend(NSObject, @selector(alloc));
objc_msgSend(obj, @selector(init));
objc_release(obj);
由此可见:
变量作用域结束时通过objc_release释放对象。
编译器自动插入了release。
{
id __weak obj1 = obj;
}
编译后的模拟代码:
id obj1;
objc_initWeak(&obj1, obj);
objc_destroyWeak(&obj1);
释放对象时,废弃谁都不持有的对象的同时,程序的动作是怎么的呢?
对象通过objc_release函数释放。
- objc_release
- 因为引用计数为0,所以执行dealloc
- _objc_rootDealloc
- object_dispose
- objc_destructInstance
- objc_clear_deallocating
对象被废弃时,最后调用的objc_clear_deallocating函数的动作如下:
- 从weak表中获取废弃对象的地址为键值的记录
- 将包含在记录中的所有附有__weak修饰符变量的地址,赋值为nil
- 从weak表中删除该记录
- 从引用计数表中删除废弃对象的地址为键值的记录