当一个 __weak 类型的指针指向的对象被释放时,该指针会自动被置成nil,因此__weak关键字修饰的指针又被称为智能指针。那么这个功能是如何实现的呢?
id __weak obj1 = obj;
会转化为
id obj1;
objc_initWeak(&obj1, obj);
objc_destoryWeak(&obj1);
即编译器会通过objc_initWeak函数初始化__weak修饰的变量,当变量的作用域结束后会通过objc_destoryWeak函数释放该变量。objc_initWeak函数实际干的活是:
objc1 = 0;
objc_storeWeak(&obj1, obj);
这里是先将指针objc1置成0,再调用objc_storeWeak函数使得obj1指向obj对象。
接下来的objc_destoryWeak函数的实际操作如下:
objc_storeWeak(&obj1, 0);
也就是说,让obj1指针指向的内容变成空。
__weak实现原理
实际上,objc_storeWeak函数会把第二个参数的对象的地址作为key,并将第一个参数(__weak关键字修饰的指针的地址)作为值,注册到weak表中。如果第二个参数为0(说明对应的对象被释放了),则将weak表中将整个key-value键值对删除,这就是__weak关键字的核心思想!
weak表和引用计数表类似,都是通过hash表实现的。如果使用weak表,将被释放的对象地址作为key去检索,就能很高效的获取对应的指向该对象的类型为__weak的指针变量的地址。同时很容易理解,一个对象可能有多个__weak指针指向,因此一个对象地址key可能对应多个值。
在调用对象的release方法时,会在其中一步调用objc_clear_deallocating函数,该函数会执行以下操作:以当前对象的地址作为key,从weak表中获取对应的值----指向该对象的__weak类型的指针变量;将取到的所有指针变量的值赋值为nil;从weak表中删除该key对应的整条记录。
如果大量使用附有__weak修饰符的变量会消耗响应的CPU资源,因此,应该尽量少使用__weak修饰符。