c语言程序中weak,浅谈Objective-C中的weak那些事

本文深入探讨了Objective-C中weak关键字的工作原理。通过runtime维护的一张hash表,存储对象地址及其所有weak指针地址,确保对象释放时,weak指针自动置为nil,避免野指针问题。

在objective-c中,weak几乎无处不在。尤其是定义ivar时,经常要用到这个关键字。用weak修饰的变量,在对象释放之后,对象会自动置为nil。它是怎么做到的呢?在研究它之前,有几个词有必要先了解一下。

关键词

SideTable

SideTable是一个结构体,主要有三个成员。它的作用就是用来管理对象的weak表,引用计数在此先不表。

struct SideTable {

// 自旋锁

spinlock_t slock;

// 引用计数的hash表

RefcountMap refcnts;

// 存储对象弱引用指针的hash表

weak_table_t weak_table;

...

}

复制代码

其中weak表(weak_table)是实现weak功能的核心数据结构。其实也可以理解SideTable是对weak_table的一个封装,方便更好的使用和访问weak表。

weak表

weak表是一个hash表,存储了弱引用对象以及相关的所有弱引用的信息。key为对象的地址,value为指向该对象的weak指针地址数组。

/**

* The global weak references table. Stores object ids as keys,

* and weak_entry_t structs as their values.

*/

struct weak_table_t {

weak_entry_t *weak_entries;

size_t num_entries;

uintptr_t mask;

uintptr_t max_hash_displacement;

};

复制代码

抽象一点的话可以像下面这么理解:

f(对象的地址) = 指向该对象的weak指针地址数组

weak_entry_t

weak_entries也是一个hash结构体,它存储的是指向绑定的弱引用对象的所有weak指针的地址。

/**

* The internal structure stored in the weak references table.

* It maintains and stores

* a hash set of weak references pointing to an object.

* If out_of_line_ness != REFERRERS_OUT_OF_LINE then the set

* is instead a small inline array.

*/

#define WEAK_INLINE_COUNT 4

// out_of_line_ness field overlaps with the low two bits of inline_referrers[1].

// inline_referrers[1] is a DisguisedPtr of a pointer-aligned address.

// The low two bits of a pointer-aligned DisguisedPtr will always be 0b00

// (disguised nil or 0x80..00) or 0b11 (any other address).

// Therefore out_of_line_ness == 0b10 is used to mark the out-of-line state.

#define REFERRERS_OUT_OF_LINE 2

struct weak_entry_t {

DisguisedPtr referent;

union {

struct {

weak_referrer_t *referrers;

uintptr_t out_of_line_ness : 2;

uintptr_t num_refs : PTR_MINUS_2;

uintptr_t mask;

uintptr_t max_hash_displacement;

};

struct {

// out_of_line_ness field is low bits of inline_referrers[1]

weak_referrer_t inline_referrers[WEAK_INLINE_COUNT];

};

};

...

};

复制代码

weak的实现原理

初始化

fc21e5d3d093e0b2a301dbe25faa8543.png

①初始化时,runtime调用objc_initWeak函数,初始化一个新的weak指针指向对象的地址。

②objc_initWeak函数会调用storeWeak函数。更新指针指向,创建弱引用表。

③如果weak指针之前指向了一个弱引用对象,则调用weak_unregister_no_lock函数接触该对象和weak指针的绑定。

④如果weak指针需要指向一个新的弱引用对象,则调用weak_register_no_lock函数把weak指针地址添加到以该对象的地址为key的弱引用表里,从而实现绑定。

释放

fdf394bb41ae7dd02735032f97509372.png

①对象释放时调用release函数。

②然后调_objc_rootRelease函数。

③接着调rootRelease函数。

④引用计数为0时,执行dealloc函数。

⑤然后调_objc_rootDealloc函数。

⑥接着调rootDealloc函数。

⑦接着调object_dispose函数。

⑧接着调用objc_destructInstance函数。

⑨接着就是一个比较重要的函数clearDeallocating。

⑩接着调用clearDeallocating_slow函数。

clearDeallocating中有两个分支,现实判断对象是否采用了优化isa引用计数,如果没有的话则需要清理对象存储在SideTable中的引用计数数据。如果对象采用了优化isa引用计数,则判断是都有使用SideTable的辅助引用计数(isa.has_sidetable_rc)或者有weak引用(isa.weakly_referenced),符合这两种情况中一种的,调用clearDeallocating_slow方法。

⑪最后调用关键的weak_clear_no_lock函数。从表中通过释放对象的地址拿到存放weak指针地址的弱引用表,然后将所有weak指针地址指向的值赋为nil,并从表中删掉该条记录。

总结

weak功能的实现主要就是通过runtime维护一张hash表,在表里存储对象的地址和指向它的所有weak指针的地址。当存储对象被释放时,通过weak_clear_no_lock函数遍历存储weak指针地址的数组,把weak指针置空,从而避免野指针的问题。(因为对象一般分配在堆上的某块内存,如果在后续的内存分配中,刚好分到了这块地址,程序就会崩溃掉;而基础数据类型一般分配在栈上,栈的内存会由系统自己自动处理,不会造成野指针)

参考

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符  | 博主筛选后可见
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值