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_autoreleaseReturnValueobjc_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函数释放,流程

  1. objc_release
  2. 引用计数为0所以执行dealloc
  3. _objc_rootDealloc
  4. objc_dispose
  5. objc_destructInstance
  6. objc_clear_deallocating

其中objc_clear_deallocating函数的动作如下:

  1. 从表中获取废弃对象的地址为键值的记录
  2. 将包含在记录中的所有附有__weak修饰符变量的地址,赋值为nil
  3. 从weak表中删除该记录
  4. 从引用计数表中删除废弃对象的地址为键值的记录

此时,当附有__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修饰符的变量时,增加了

  1. objc_loadWeakRetained函数 取出__weak修饰的变量并且retain
  2. objc_autorelease函数将对象注册到autoreleasepool中

因此在使用__weak修饰的变量所引用的对象,被注册到autorelasepool中,在@autorelase结束之前都可以放心使用

注意

当 大量使用 weak修饰修饰的对象时,就会大量注册到autorelasepool中对象,因此在使用__weak修饰符修饰的对象时,尽量先赋值给 __strong修饰符的对象再使用,这样就只注册到autoreleasepool中一次

3. 特殊的不能使用__weak的情况

  1. NSMachPort类不支持__weak修饰符 这些类重写了retain/relesase并且实现独自的引用计数
  2. 不支持__weak修饰符的类,其类声明中 附加了__attribute__((objc_arc_weak_reference_unavailabel))
  3. 类自己实现了 allocsWeakReference方法返回NO 则不能使用__weak修饰符
  4. 类自己实现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)