assign 和 weak,weak 自动置空原理

MRC时代使用unsafe_unretained。ARC时候才有weak。

1、assign可以修饰对象和基本数据类型( int,  BOOL ),  weak只修饰对象

2、assign 所修饰的对象被释放后,还会指向原对象内存地址, 会产生悬垂指针 。weak 所修饰的对象被废弃之后,weak 所修饰对象会被设置为nil。

对象在堆上容易造成崩溃。而栈上的内存系统会自动处理,不会造成野指针。

assign 和 weak  都不会改变修饰对象的引用计数。

unsafe_unretained也可能产生野指针,所以它名字是"unsafe”

MRC中使用retain、release ,assign 。weak和strong就只能在ARC中使用

weak其实类似于assign,叫弱引用,也是不增加引用计数。一般只有在防止循环引用时使用,比如父类引用了子类,子类又去引用父类。IBOutlet、Delegate一般用的就是weak。

weak 和 assign 的区别在于 weak在对象没有引用的时候会自动进行置空操作,引用计数变成0 时会把指针置为nil ,不会出现野指针错误,但是assign 在对象释放之后,指向对象的指针还是存在的,此时再对这个指针进行操作就会出现崩溃,野指针错误。

相对的,strong就类似与retain了,叫强引用,会增加引用计数,类内部使用的属性一般都是strong修饰的,现在ARC已经基本替代了MRC,所以我们最常见的就是strong了。

ARC中用是否有强指针引用来判断对象是否需要销毁,对象创建以后默认情况下全是强指针 ,加上_weak修饰的时候表示对象为弱指针。只要没有任何强指针指向对象,对象就会被销毁。

runtime对注册的类,会进行布局,对于weak对象会放入一个hash表中,用weak指向的对象内存地址作为key,当此对象的引用计数为0 的时候会dealloc,会调用clearDeallocating函数,clearDeallocating函数首先根据对象地址获取所有weak指针地址的数组,然后遍历这个数组把其中的数据设为nil,最后把这个entry(weak修饰的指针)从weak表中删除,最后清理对象(weak指针指向的对象)的记录。



weak属性修饰的变量,如何实现在变量没有强引用后自动置为 nil

runtime 维护了一个弱引用表,在对象回收的时候,根据对象的地址得到所有weak指针地址的数组,遍历数组把其中的数据置为nil,释放weak_entry_t(指针)对象;并且把side_table中的对象(指针指向的对象)进行clean;

一、数据结构

SideTables本质上是系统中全局唯一的 StripedMap,管理对象的引用计数和weak引用指针,每个对象在此表中都有对应的一个 SideTable。

StripedMap本质是一个数组,且在iOS系统下,容量为64。通过实 [  ]  操作,实现了类似字典的功能:可通过传入一个对象作为key值,来获取对应的Item。

在 SideTables中, Item 类型为 SideTable,对于任何一个对象, SideTables都能根据其地址对应到具体的一个 SideTable上。objTable = &SideTables()[obj] : 通过对象地址 获取到其对应的SideTable

1) SideTable

SideTable

slock: 操作时,对 SideTable 加锁(自旋锁)防止其他访问。

refcnts:对象的引用计数器,存储对象引用计数的map。key :对象地址,value:对象的引用计数值 - 1.

引用计数相关:

weak_table_t:  存储对象弱引用(weak 引用)的全局散列表。key :对象地址,value:所有指向该对象的 weak 指针。

2) weak_table 表:根据对象地址获取到对象在 weak_table 表中的 weak_entries。

weak_table_t

weak_entries:存放 对象 与 弱引用指针数组 映射关系的

num_entries:存放的弱引用 总数

mask:可存储弱引用的容量

max_hash_displacement:最大哈希偏移值

weak_entry_t:存储在弱引用表中的一个内部结构体,它负责维护和存储指向一个对象的所有弱引用hash表。

本质上是个字典。 其中的 key 值为对象, value 对应为一个数组,该数组最初为内部的一个大小为4 的数组 (inline_referrers ),当数组大小超过4后,则变为内部一个可变大小数组( referrers )。数组中保存的值均为 weak_referrer_t 类型的数据。

weak_entry_t 结构体

referent  是引用对象。

union(联合体) 里存放着弱引用该对象的指针,union 里面的多个成员变量共享同一内存空间。union 中有两个结构体都是存储弱引用对象指针的集合。

第1个结构体referrers 是一个可进行扩容的集合,

第2个结构体中 inline_referrers 是一个容量为 4 的数组。不可扩容,弱引用指针优先存放这里

weak_entry_t 默认使用 inline_referrers 来保存弱引用指针,当此数组容量满后,会使用 referrers 接管保存工作。out_of_line_ness 便是描述存储的弱引用指针是否超出 inline_referrers 的容量。


weak_referrer_t

本质上是 objc_object **,即Objective-C对象的地址。

weak_entry_t 类型的  value 数组中,每一个 Item 均为一个地址,即weak对象的地址。

对应关系:

对应关系图

1. SideTables 一对多 SideTable :每个对象都有一个SideTable与之对应

2. SideTable 一对一 weak_table :一个SideTable可能存储了多个对象的弱引用信息

3. weak_table 一对多  weak_entry :对象在weak_table中 找到自己的弱引用关系数据列表weak_entry

4. weak_entry 一对多 weak_referrer :存储每一个指向 对象的 弱引用指针地址对象,可在数组中找到自己(当前弱引用指针)所在的位置,进行删除,更新等操作

每个对象都会在全局的 SideTables 中对应至一个 SideTable中, SideTable中的 weak_table_t 记录了该 SideTable下所有内存对象的 weak 引用信息,内存对象可在 weak_table_t  中找到与自己内存地址 对应的  weak_entry_t , weak_entry_t  中记录了所有指向该内存对象且weak修饰的对象信息。


 runtime 如何实现 weak 变量的自动置nil?

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

objc_initWeak

2、添加引用时:objc_initWeak 函数会调用 objc_storeWeak() 函数,objc_storeWeak() 的作用是更新指针指向(指针可能原来指向着其他对象,这时候需要将该 weak 指针与旧对象解除绑定,会调用到 weak_unregister_no_lock),如果指针指向的新对象非空,则创建对应的弱引用表,将 weak 指针与新对象进行绑定,会调用到 weak_register_no_lock。在这个过程中,为了防止多线程中竞争冲突,会有一些锁的操作。

objc_storeWeak

3  对象释放时:调用 clearDeallocating 等一系列函数,大概操作如下:

1.  用对象地址作为 key 在  sidetables  中找到对象对应的  sidetable 

2.  在  sidetable  的  weak_table 数组  中找到自己的  weak_entry ,然后遍历数组把所有的数据设为 nil

3.  释放weak_entry对象,把 weak_entryweak_table 数组中删除(该对象对应的weak_entry弱引用数组表被整体移除)

4. 如weak_table 数组内无值,把  sidetable 中的对象记录进行clean;

首先,通过 weak_entry_for_referent 找到 weak_table 中的弱引用条目 entry,然后通过 remove_referrer 函数从 entry 的引用指针列表中删除 __weak变量指针。如果 entry 中没有引用指针了,那么便会执行 weak_entry_remove 从弱引用表 weak_table 中删除该弱引用条目。

如果,entry 的引用指针数不超过 inline_referrers 的容量,那么遍历 inline_referrers 找到引用指针的位置并置为nil。如果超过 inline_referrers 的容量,那么便得去 referrers 中找到引用指针置为nil并将 referrers 的长度减一。如果找不到便会调用 objc_weak_error() , 此函数将弱引用条目 entry 从 weak_table 中删除,然后通过 weak_compact_maybe 去检查是否需要缩小 weak_table 的容量。

weak_entry_t在移除对象后,并不会进行类似 weak_table_t压缩数据结构的操作,故应尽量保证weak对象个数较少。


参考:

《iOS之一起进大厂》系列-iOS属性关键字和相关的面试题 - 简书

OC底层探索19-weak和assign区别浅谈 - 简书

iOS weak的实现原理和load和initialize的区别 - 简书

__weak 修饰符 - 简书

iOS weak原理源码探究 - 简书

iOS底层学习 - 内存管理之weak原理探究  👍👍 👍 

iOS weak关键字实现原理_TuGeLe的博客-CSDN博客 👍👍 👍 👍👍

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 218,546评论 6 507
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,224评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,911评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,737评论 1 294
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,753评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,598评论 1 305
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,338评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,249评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,696评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,888评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,013评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,731评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,348评论 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,929评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,048评论 1 270
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,203评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,960评论 2 355

推荐阅读更多精彩内容

  • 很少有人知道weak表其实是一个hash(哈希)表,Key是所指对象的地址,Value是weak指针的地址数组。更...
    1bf235f48fa8阅读 528评论 0 0
  • weak功能就不多说了,它的实现原理就从一段代码开始吧。一个OC变量的默认属性都是strong,所以我们如果需要w...
    随风流逝阅读 2,364评论 0 3
  • 前言 提起弱引用,大家都知道它的作用:(1)不会添加引用计数 (2)当所引用的对象释放后,引用者的指针自动置为ni...
    无忘无往阅读 668评论 0 4
  • 前言 大家都知道,weak 关键字的作用 弱引用,所引用对象的计数器不会加一,并在引用对象被释放的时候自动被设置为...
    6ffd6634d577阅读 914评论 0 3
  • 面试的时候,经常会问这个,之前面试回答的很简单,就是:底层有个hash表专门来维护存储weak指针,当指向的对象的...
    huxinwen阅读 1,680评论 1 6