美文网首页
《Objective-C高级编程 iOS与OS X多线程与内存管

《Objective-C高级编程 iOS与OS X多线程与内存管

作者: 我才是臭吉吉 | 来源:发表于2019-01-06 16:15 被阅读7次

内存管理篇:8.ARC实现之__weak

我们知道,weak修饰的变量,其地址是被存储在一个专用的散列表中,此散列表的键值为原内存的散列值(即对象地址的hash值)。

weak表.jpg

__weak修饰的变量,其主要存在两大功能:

  1. 当其引用的对象被释放时,此变量自动被赋值为nil
  2. 使用__weak修饰的变量时,实际上使用的是添加到autoreleasePool中的对象

当__weak变量引用的对象被释放时,其自动被赋值为nil

先看示例:

{
    // obj为强引用变量且已经被赋值
    id __weak obj1 = obj;
}

编译后的模拟代码为:

id obj1;
objc_initWeak(&obj1, obj);
objc_destroyWeak(&obj1);

objc_initWeak方法,即为:

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

objc_destroyWeak方法,即为:

objc_storeWeak(&obj1, 0);

由此可以看出,

  1. 对于初始化的weak变量:为变量赋值为0,并将其地址存储到weak表中,对象内存的散列值所在的地址中
  2. 释放weak变量时:则是在weak表中,将对象内存散列值所在地址存储的数据中,将存储的变量清空

由于可以使用多个weak变量指向同一对象,所以在weak表中,一个键值的散列地址中可以存储多个weak变量地址。

结论:
weak对象释放时,自动置为nil的原因

  • 在系统调用dealloc释放对象时(最后的objc_clear_deallocating方法),ARC会根据对象的引用状态,去weak表中查询对应的weak变量,将变量地址赋值为nil,并将其记录删除。最后将此键值记录一并删除。
  • 注意:此过程需要消耗CPU资源,故不要滥用,需要时再使用(如避免delegate和block导致的引用循环)。

使用__weak修饰的变量时,实际上使用的是添加到autoreleasePool中的对象

还是先看实例:

{
    // obj为强引用变量,且已被赋值
    id __weak obj1 = obj;
    NSLog(@"%@", obj1);
}

编译后的模拟代码为:

// 初始化weak变量并赋值
id obj1;
objc_initWeak(&obj1, obj);
// ???
id tmp = objc_loadWeakRetained(&obj1);
// ???
objc_autorelease(tmp);
NSLog(@"%@", tmp);
// 释放weak变量
objc_destroyWeak(&obj1);

其中:

  1. 先通过objc_loadWeakRetained方法,将weak变量对应引用的对象取出,并进行retain操作。
  2. 使用objc_autorelease方法,将生成的临时对象加入到autoreleasePool中,即可保证对象的生存期,以便正常使用。

注意:

  • 大量使用weak变量,会在autoreleasePool中插入大量临时变量,增加内存开销,并对CPU进行过多无畏的损耗。
  • 正确的使用方法,是使用strong变量指向weak变量后,再进行使用。
id __weak obj1 = obj;
id tmp = obj1;
NSLog(@"1.%@", tmp);
NSLog(@"2.%@", tmp);
NSLog(@"3.%@", tmp);
NSLog(@"4.%@", tmp);
NSLog(@"5.%@", tmp);
// 这样,autoreleasePool只会将obj1插入一次

相关文章

网友评论

      本文标题:《Objective-C高级编程 iOS与OS X多线程与内存管

      本文链接:https://www.haomeiwen.com/subject/tfvsrqtx.html