3-weak原理

本文详细剖析了Objective-C中weak引用的实现机制,涉及SideTable结构、weak_table_t和weak_entry_t的使用,以及weak引用的创建、移除和在dealloc中的清除过程。重点介绍了storeWeak和remove_referrer等关键函数的工作原理。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档


前言

weak是一种防止循环引用,也能在对象回收时自动置为nil的引用,下面从源码角度来分析weak的实现原理。

一、SideTable介绍

SideTable是用于存储扩展的引用计数和弱引用集合的结构体,SideTable的结构体:
在这里插入图片描述

  • slock 自旋锁,用于上锁/解锁 SideTable。
  • refcnts 参考第三节
  • weak_table 存储对象弱引用指针的hash表。

1.1 weak_table_t(objc_weak.h)

weak_table_t结构体如图
在这里插入图片描述

  • weak_entries: hash数组,用来存储弱引用对象的相关信息weak_entry_t
  • num_entries: hash数组中的元素个数
  • mask:hash数组长度-1,会参与hash计算。(注意,这里是hash数组的长度,而不是元素个数。比如,数组长度可能是64,而元素个数仅存了2个)
  • max_hash_displacement:可能会发生的hash冲突的最大次数,方便hashtable扩展。

1.2 weak_entry_t(objc_weak.h)

weak_entry_t也是一个hash结构,占位16 * 4,key是对象地址,value是弱引用指针集合。通过操作指针的指针,就可以使得weak 引用的指针在对象析构后,指向nil。weak_entry_t结构体如图所示。
在这里插入图片描述
从图中可以看出weak_entry_t是个共用体,占用4个字节。在联合体的内部有定长数组inline_referrers[4]和动态数组*referrers两种方式来存储弱引用对象的指针地址。通过out_of_line()这样一个函数方法来判断采用哪种存储方式。当弱引用该对象的指针数目小于等于4时,使用定长数组。当超过4时,会将定长数组中的元素转移到动态数组中,并之后都是用动态数组存储。

二、weak引用本质

weak初始化方法调用objc_initWeak(NSObjct.mm),代码如下:
在这里插入图片描述

  • location是_weak指针的指针,后续方便置为nil。
  • newObj是对象的指针。
    weak引用destroy调用objc_destroyWeak,代码如下:
    在这里插入图片描述
  • location是_weak指针的指针,后续方便置为nil。

2.1 weak指针的入口storeWeak

这里删除多线程操作和初始化检查代码,简化后代码如下:
在这里插入图片描述
storeWeak接收的5个参数:

参数名备注
haveOldweak指针之前是否指向了一个弱引用
haveNewweak指针是否需要指向一个新的引用
crashIfDeallocating如果被弱引用的对象正在析构,此时再弱引用该对象是否应该crash。
locationweak指针的指针
newObj对象的指针

由上图可以看出:

  • 如果weak指针之前指向了一个弱引用,则会调用weak_unregister_no_lock 方法将旧的weak指针地址移除。
  • 如果weak指针需要指向一个新的引用,则会调用weak_register_no_lock 方法将新的weak指针地址添加到弱引用表中。
  • 使用setWeaklyReferenced_nolock将isa中weakly_referenced位置1,表示对象由weak引用。

三、weak引用创建(objc-weak.mm)

weak引用的创建入口是weak_register_no_lock,这里去除了判nil和一些异常判断的代码,具体代码如下:
在这里插入图片描述
weak_register_no_lock接收的4个参数:

参数名备注
*weak_tableweak_table_t 结构类型的全局的弱引用表。
referent_id对象指针
*referrer_idweak指针的指针
crashIfDeallocating如果被弱引用的对象正在析构,此时再弱引用该对象是否应该crash。

由上图可以看出:

  • 用对象指针得到weak_entry,有的话将weak指针的指针直接存入,没有的话创建weak_entry,再插入weak指针的指针。

3.1 插入weak_entry(append_referrer)

对append_referrer方法注解如下:
在这里插入图片描述

四、weak引用移除(objc-weak.mm)

weak引用的移除入口是weak_unregister_no_lock,具体代码如下:
在这里插入图片描述
weak_unregister_no_lock接收的3个参数:

参数名备注
*weak_tableweak_table_t 结构类型的全局的弱引用表。
referent_id对象指针
*referrer_idweak指针的指针

4.1 删除weak(remove_referrer)

对remove_referrer方法代码注解如下:
在这里插入图片描述

五、dealloc对weak的清除

dealloc的前序调用参考2节。dealloc对weak的清除入口clearDeallocating方法(objc-object.h)如下:
在这里插入图片描述
对clearDeallocating_slow的方法(NSObject.mm)调用如下:
在这里插入图片描述
由上图可知真正删除weak引用是调用weak_clear_no_lock这个方法,代码注解如下:
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值