心辰·Dev

OC 内存管理机制 ARC 的底层实现

ARC (Automatic Reference Counting),即主要由编译器进行自动的内存管理。 ARC 的实现需要 clang (LLVM编译器)和 objc4 Objective-C 运行时库配合进行。

__strong 修饰符的实现

使用 alloc/new/copy/mutableCopy 方法

赋值给附有 __strong 修饰符的变量:

1
2
3
{
id __strong obj = [[NSObject alloc]init];
}

将上述代码转换为汇编代码进行分析,发现实际上可简化为调用以下函数:

1
2
3
id obj = objc_msgSend(NSObject, @selector(alloc));
objc_msgSend(obj, @selector(init));
objc_realease(obj);

由此可知编译器在变量作用域结束时通过自动插入 objc_release 释放了对象。

使用其他方法

1
2
3
{
id __strong obj = [NSMutableArray array];
}

替换代码后发现结果与之前不同:

1
2
3
id obj = objc_msgSend(NSMutableArray, @selector(array));
objc_retainAutoreleasedReturnValue(obj);
objc_realease(obj);

NSMutableArray 类的 array 类方法调用后,会由编译器插入 objc_retainAutoreleasedReturnValue 函数,主要用来最优化程序运行。

1
2
3
+(id) array {
return [[NSMutableArray alloc]init];
}

转换以上代码后发现源代码使用了 objc_autoreleaseReturnValue 函数。

1
2
3
4
5
+(id) array {
id obj = objc_msgSend(NSMutableArray, @selector(alloc));
objc_msgSend(obj, @selector(init));
return objc_autoreleaseReturnValue(obj);
}

objc_autoreleaseReturnValue 和 objc_retainAutoreleasedReturnValue 函数成对出现,如果在 autoreleaseReturnValue 之后直接调用了 retainAutoreleasedReturnValue ,则不再将返回的对象注册到 autoreleasepool 中,而是直接传递到函数的调用方。这一过程达到了最优化。

__weak 修饰符的实现

__weak 修饰符的主要作用是:

  • 当附有 __weak 修饰符的变量所引用对象被废弃,则将 nil 赋给该变量。
  • 使用附有 __weak 修饰符的变量,即是使用注册到 autoreleasepool 中的对象。

引用对象被废弃则自动赋值 nil

当 __strong 修饰的对象赋值给 __weak 变量:

1
2
3
{
id __weak obj1 = obj;
}

源代码的实现可模拟为:

1
2
3
id obj1;
objc_initWeak(&obj1, obj);
objc_destroyWeak(&obj1);

其中, initWeak 函数的作用可模拟为:

1
2
obj1 = 0;
objc_storeWeak(&obj1, obj);

而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
2
3
4
{
id __weak obj1 = obj;
NSLog(@"%@", obj1);
}

该源码可转换为以下形式:

1
2
3
4
5
6
id obj1;
objc_initWeak(&obj1, obj);
id tmp = objc_loadWeakRetained(&obj1);
objc_autorelease(tmp);
NSLog(@"%@", tmp);
objc_destroyWeak(&obj1);

其中, objc_loadWeakRetained 取出 __weak 变量所引用的对象并 retain 。 objc_autorelease 将对象注册到 autoreleasepool 中。
如果大量使用 __weak 变量,注册到 autoreleasepool 中的对象也会大量地增加,因此在使用 __weak 变量时,最好先暂时赋值给 __strong 变量后再使用。