问题:Runtime如何实现weak变量的自动置nil?
-
weak属性的特点:
weak
表明该属性定义了一种“非拥有关系” (nonowning relationship)。
为weak
属性设置新值时,设置方法既不保留新值,也不释放旧值。
同assign
类似,然而在属性所指的对象释放时候,属性值也会清空(nil out)。
-
runtime是如何实现 weak 变量的自动置nil?
Runtime 对注册的类,会进行内存布局,从一个粗粒度的概念上来讲,这时候weak
对象会放入一个 hash 表中,这是一个全局表,表中是用 weak
指向的对象内存地址作为key
,用所有指向该对象的 weak 指针表作为value
。当此对象的引用计数为 0 的时候会 dealloc
,假如该对象内存地址是 address
,那么就会以 address
为key
,在这个weak
表中搜索,找到所有以 address
为键的weak
对象,从而设置为nil
。
-
runtime 如何实现 weak 属性具体流程大致分为 3 步:
- 初始化时:runtime 会调用
objc_initWeak
函数,初始化一个新的 weak 指针指向对象的地址。 - 添加引用时:
objc_initWeak
函数会调用objc_storeWeak(&weakPo, Model)
函数。
objc_storeWeak(&weakPo, Model)
的作用是更新指针指向(指针可能原来指向着其他对象,这时候需要将该weak
指针与旧对象解除绑定,会调用到weak_unregister_no_lock)
,如果指针指向的新对象非空,则创建对应的弱引用表,将 weak 指针与新对象进行绑定(即把赋值对象(Model)的内存地址作为键值key,将weak修饰的属性变量(weakPo)的内存地址(& weakPo)作为value
),会调用到weak_register_no_lock
,注册到weak
表中。在这个过程中,为了防止多线程中竞争冲突,会有一些锁的操作。
如果Model
为0(nil),那么把变量weakPo
的内存地址&weakPo
从weak
表中删除,可以把objc_storeWeak(&weakPo, Model)
理解为:objc_storeWeak(value, key)
,并且当key
变nil
,将value
置nil
。
在Model
非nil
时,weakPo
和Model
指向同一个内存地址,在Model
变nil
时,weakPo
变nil
。此时向weakPo
发送消息不会崩溃:在Objective-C中向nil发送消息是安全的
- 释放时:调用 clearDeallocating 函数,clearDeallocating 函数首先根据对象地址获取所有 weak 指针地址的数组,然后遍历这个数组把其中的数据设为 nil,最后把这个 entry 从 weak 表中删除,最后清理对象的记录。