ARC的实现
苹果官方说明中称,ARC是由编译器进行内存管理,但实际上只有编译器是无法完全胜任的,还需要Object-C
运行时库的协助
__strong修饰符实现
赋值给__strong
修饰符在程序中实际运行
{
id __strong obj = [[NSObject alloc] init]
}
//编译器的模拟代码
id obj = objc_msgSend(NSObject, @selector(alloc))
objc_msgSend(obj, @selector(init))
objc_release(obj)
编译器在变量超出作用域时 自动插入了release方法
调用alloc/new/copy/mutableCopy
以外开头的方法,
+(id) array {
return [[NSMUtableArray alloc] init]
}
//编译器的模拟代码
+ (id)array {
id obj = objc_msgSend(NSMutableArray, @selector(alloc))
objc_msgSend(obj, @selector(init))
return objc_autoreleaseReturnValue(obj)
}
{
id __strong obj = [NSMutableArray array];
}
//编译器的模拟代码
id obj = objc_msgSend(NSMutableArray, @selector(array))
objc_retainAutoreleasedReturnValue(obj)
objc_release(obj)
objc_retainAutoreleasedReturnValue()
和objc_autoreleaseReturnValue()
是成对存在的,用于最优化程序运行
objc_autoreleaseReturnValue
与objc_autorelease
函数不同,其功能不仅仅是指注册对象到autoreleasepool
中,而是会检查使用该函数的方法或函数调用方的执行命令列表,如果调用方在调用了该函数时接着调用了objc_retainAutoreleaseReturnValue()
函数,那么就不将函数注册到autoreleasepool中,而是直接传递到方法或函数的调用方。
objc_retainAutoreleasedReturnValue
函数与objc_retain
函数不通过,即使不注册到autoreleasepool中而返回对象也能正确获取函数
__weak修饰符
{
id __weak obj1 = obj;
}
//模拟源代码
id obj1;
objc_initWeak(&obj1, obj)
objc_destroyWeak(&obj1)
initWeak初始化附有__weak修饰符的变量,其等效于
objc_initWeak(&obj1, obj)
//等效于 将__weak修饰符变量初始化为0后,调用storeWeak
obj1 = 0
objc_storeWeak(&obj1, obj)
objc_destroryWeak 函数 释放该变量 等效于
objc_destroyWeak(&obj1)
//等效于
objc_storeWeak(&obj1, 0)
objc_storeWeak()
函数 将第二参数的赋值对象的地址作为键值,将第一参数附有__weak修饰符的变量地址注册到weak
表中。如果第二参数为0 则将变量的地址从weak表中删除
weak表与引用计数表相同 作为hash表(散列表)实现。由于一个对象可以同时赋值给多个附有__weak修饰符的变量,因此将废弃地址的键值进行搜索就能高速获取所有对应的__weak修饰变量的地址
1. 若附有__weak修饰符的变量所引用的对象被废弃,则将nil赋值给该变量
当废弃没人持有的对象时,对象通过objc_release函数释放,流程
- objc_release
- 引用计数为0所以执行dealloc
- _objc_rootDealloc
- objc_dispose
- objc_destructInstance
- objc_clear_deallocating
其中objc_clear_deallocating
函数的动作如下:
- 从表中获取废弃对象的地址为键值的记录
- 将包含在记录中的所有附有__weak修饰符变量的地址,赋值为nil
- 从weak表中删除该记录
- 从引用计数表中删除废弃对象的地址为键值的记录
此时,当附有__weak修饰符引用的独享被废弃时,该变量会被自动置空为nil
注意
由上面步骤可知,当大量使用附有 __weak修饰符的变量时,会消耗相应的cpu资源。因此尽量在只需要避免循环引用时使用 __weak修饰符
2. 使用附有__weak修饰符变量,即是访问注册到autoreleasepool中的对象
{
id __weak obj1 = obj;
NSLog(@“%@”, obj1)
}
//模拟代码
id obj1;
objc_initWeak(&obj1, obj)
id tmp = objc_loadWeakRetained(&obj1)
objc_autorelease(tmp)
NSLog(@"%@", tmp)
objc_destroyWeak(&obj1)
在使用__weak修饰符的变量时,增加了
objc_loadWeakRetained
函数 取出__weak修饰的变量并且retainobjc_autorelease
函数将对象注册到autoreleasepool中
因此在使用__weak修饰的变量所引用的对象,被注册到autorelasepool中,在@autorelase结束之前都可以放心使用
注意
当 大量使用 weak修饰修饰的对象时,就会大量注册到autorelasepool中对象,因此在使用__weak修饰符修饰的对象时,尽量先赋值给 __strong修饰符的对象再使用,这样就只注册到autoreleasepool中一次
3. 特殊的不能使用__weak的情况
- NSMachPort类不支持__weak修饰符 这些类重写了retain/relesase并且实现独自的引用计数
- 不支持__weak修饰符的类,其类声明中 附加了
__attribute__((objc_arc_weak_reference_unavailabel))
- 类自己实现了
allocsWeakReference
方法返回NO 则不能使用__weak修饰符 - 类自己实现
retainWeakReference
方法 返回NO 则该变量为nil
__autoreleasing修饰符
将对象赋值给附有__autoreleasing修饰符 相当于ARC无效时调用对象的autorelase方法
@autoreleasepool{
id __autorelasing obj = [[NSObject alloc] init]
}
//模拟源代码
id pool = objc_autoreleasePoolPush()
id obj = objc_msgSend(NSObject, @selector(alloc))
objc_msgSend(obj, @selector(init))
objc_autorelase(obj)
objc_autoreleasepoolPop(pool)
调用alloc/new/copy/mutableCopy
之外的方法
@autorelasepool{
id __autoreleasing obj = [NSMutableArray array]
}
//编译器的模拟代码
id pool = objc_autoreleasePoolPush()
id obj = objc_msgSend(NSMutableArray, @selector(array))
objc_retainAutoreleaseReturnValue(obj)
objc_autorelase(objc)
objc_autoreleasepoolPop(pool)