ARC (Automatic Reference Counting),即主要由编译器进行自动的内存管理。 ARC 的实现需要 clang (LLVM编译器)和 objc4 Objective-C 运行时库配合进行。
__strong 修饰符的实现
使用 alloc/new/copy/mutableCopy 方法
赋值给附有 __strong 修饰符的变量:
1 | { |
将上述代码转换为汇编代码进行分析,发现实际上可简化为调用以下函数:
1 | id obj = objc_msgSend(NSObject, @selector(alloc)); |
由此可知编译器在变量作用域结束时通过自动插入 objc_release 释放了对象。
使用其他方法
1 | { |
替换代码后发现结果与之前不同:
1 | id obj = objc_msgSend(NSMutableArray, @selector(array)); |
NSMutableArray 类的 array 类方法调用后,会由编译器插入 objc_retainAutoreleasedReturnValue 函数,主要用来最优化程序运行。
1 | +(id) array { |
转换以上代码后发现源代码使用了 objc_autoreleaseReturnValue 函数。
1 | +(id) array { |
objc_autoreleaseReturnValue 和 objc_retainAutoreleasedReturnValue 函数成对出现,如果在 autoreleaseReturnValue 之后直接调用了 retainAutoreleasedReturnValue ,则不再将返回的对象注册到 autoreleasepool 中,而是直接传递到函数的调用方。这一过程达到了最优化。
__weak 修饰符的实现
__weak 修饰符的主要作用是:
- 当附有 __weak 修饰符的变量所引用对象被废弃,则将 nil 赋给该变量。
- 使用附有 __weak 修饰符的变量,即是使用注册到 autoreleasepool 中的对象。
引用对象被废弃则自动赋值 nil
当 __strong 修饰的对象赋值给 __weak 变量:
1 | { |
源代码的实现可模拟为:
1 | id obj1; |
其中, initWeak 函数的作用可模拟为:
1 | obj1 = 0; |
而destroyWeak 函数可模拟为:
1 | objc_storeWeak(&obj1, 0); |
weak 表与引用计数表一样都是作为散列表被实现的。将附有 __weak 修饰符的变量的地址作为键(key),所引用对象的地址作为键值(value)来索引。一个键值可注册多个__weak 变量的地址。
objc_storeWeak 函数把第一个参数的变量地址注册到 weak 表中,第二个参数的对象地址作为键值,如果第二个参数为 0,则把变量地址从 weak 表删除。
当释放对象时,如果该对象谁都不持有,将通过 objc_release 函数释放,过程如下:
- objc_release
- dealloc
- _objc_rootDealloc
- object_dispose
- objc_destructInstance
- objc_clear_deallocating
当最后调用 objc_clear_deallocating 时,将从 weak 表中检索以废弃对象地址为键值(value)的记录,将记录中所有用 __weak 修饰的变量地址改为 nil 。再从 weak 表中删除该记录。
自动注册到 autoreleasepool
当使用附有 __weak 的变量:
1 | { |
该源码可转换为以下形式:
1 | id obj1; |
其中, objc_loadWeakRetained 取出 __weak 变量所引用的对象并 retain 。 objc_autorelease 将对象注册到 autoreleasepool 中。
如果大量使用 __weak 变量,注册到 autoreleasepool 中的对象也会大量地增加,因此在使用 __weak 变量时,最好先暂时赋值给 __strong 变量后再使用。