美文网首页
1.3 property 关键字 —— strong、weak

1.3 property 关键字 —— strong、weak

作者: 哈库呐玛塔塔__ | 来源:发表于2020-05-13 02:20 被阅读0次

1.strong 什么时候用? 和__strong 有什么关系

strong修饰的属性一般不会自动释放;

在OC中,对象默认是强指针,在实际开发中一般属性对象用strong来修饰(NSArray,NSDictionary),在使用懒加载定义控件的时候,一般也用strong

2.什么时候用weak?和__weak有什么关系?

1)xib、sb 给控件拖线的时候,也是用weak来修饰的。 因为xib和sb是被持有的,而这些控件是被xib/sb持有的,当xib/sb需要被释放的时候,不应该有其他地方还强引用着这个控件。比如引用xib的某个VC强引用了这个控件,你连xib都不要了 还强引用它的控件干嘛?

2)在开发的时候用的代理也是用weak进行修饰的,其目的是为了防止控件的循环引用.

__weak __strong 用来在函数或方法中声明临时变量的所有权修饰符。而strong和weak用来定义属性时的属性声明。

3.weak与assign的区别,何时使用assign ,assign修饰对象会怎么样?;weak和unsafe_unretained的区别?

weak只可用来修饰对象。修饰的对象释放后指针会被置为nil,不会崩溃。

assign一般用来修饰基本类型。不过也可以修饰对象。但是在修饰对象的时候,如果对象被释放,会出现野指针错误,产生崩溃。

unsafe_unretained修饰的对象被释放后,会出现野指针错误,产生崩溃。MRC时候用的东西,ARC之后用的比较少了。

unsafe_unretained修饰基本数据类型的时候并没有报错。

unsafe_unretained修饰的属性的内存,不受编译器控制,而weak在arc下是编译器自动控制的。

weak对象在初始化的时候需要检查对象是否被释放,这会对性能有一些损耗,但其实这些损耗微乎其微。这也是unsafe_unretained和weak之间的一点区别,也是unsafe_unretained存在的意义。

__unsafe_unretained的一个使用场景:使用场景举一个例子,为什么这么用 

4.weak怎么实现自动置为nil的?

http://www.cocoachina.com/ios/20170328/18962.html

Runtime维护了一个weak表,用于存储指向某个对象的所有的weak指针。这个weak表是一个哈希表,key是所指对象的地址,value是由一个叫做weak_entry_t类型的结构体,这个结构体负责维护和存储指向一个对象的所有弱引用的hash表(weak指针的地址数组)。

当一个对象被回收的时候。经过层层调用,会最终触发下面的方法将所有Weak指针的值设为nil。

这里边我要详细说下weak的实现原理。概括一下是三步:

初始化时:runtime会调用objc_initWeak(id *object, id value)函数(object就是weak指针的地址,value就是weak指针指向的内容,__weak NSString * weakObj = objc  object就是&weakObj   value就是objc) 先来判断其指针指向的类对象是否有效,无效直接释放,有效的话会调用objc_storeWeak函数来添加他们之间的引用关系也就是指针指向。

添加引用时:objc_storeWeak()的作用是更新指针指向,创建对应的弱引用表。获取新旧引用散列,如果被赋值的对象之前有值(haveOld)先将旧散列表进行更改,调用SideTables()构造;加锁;解除旧对象与弱引用表的关联绑定;构造新的弱引用计数表。storeWeak函数首先找到weak指针指向的老对象,获取到与新旧对象相关的SideTable对象,在老对象的weak表中移除指向信息,而在新对象的weak表中建立关联信息 ,让弱引用指针指向新的对象

释放时:clearDeallocating中首先根据对象地址获取了这个对应的SideTable,调用了weak_clear_no_lock函数,然后在这个函数中在weak表里获取了所有指向这个对象的weak指针组成的数组就是weak_entry_t ,将里边所有的附有weak修饰符变量的地址,赋值为nil,将weak表中关于该对象的记录删除,引用计数表中删除废弃对象的地址为键值的记录 

        【通过一些列的函数调用,直到objc_clear_deallocating函数的调用(NSObject的dealloc 中调用objc_rootDealloc函数,接着调用objc_dispose函数,接着调用objc_destructInstance,最后调用到objc_clear_deallocating)在这个函数中调用了clearDeallocating,(clearDeallocating后又调用了weak_clear_no_lock ,它才是这里重点讨论的地方,前边都算是进入到这里的入口)】。

总之,那一系列的函数调用 我也没记住,反正最终就是在free之前做了一些判断有没有全局变量,有没有关联对象,有没有弱引用,是不是nonpointer,然后按照执行顺序先后释放c++变量,解除关联对象,循环weak表 将所有的弱引用指针修改为nil。

objc_clear_deallocating该函数的动作如下:

1、从weak表中获取废弃对象的地址为键值的记录

2、将包含在记录中的所有附有 weak修饰符变量的地址,赋值为nil

3、将weak表中该记录删除

4、从引用计数表中删除废弃对象的地址为键值的记录

has_cxx_dtor表示当前对象有 C++ 或者 ObjC 的析构器(destructor),如果没有析构器就会快速释放内存。

在这里说下看到这些资料时对全局的理解。

这里的全局其实是一个SideTables,它是一个hash表,key是对象的地址,value是一个SideTable的结构体。

每一个NSObject 都有一个这样的SideTables表 管理着这个对象下 每一 个对象

SideTable结构体中主要有三个成员变量:

SideTable管理着一个对象 的引用计数,和该对象的所有弱引用(其实不就是对一个对象的所有引用么?强弱都包含了)

4讲的其实是__weak修饰的变量被赋值时的原理,5讲的是__weak修饰的变量被使用时的原理

5.__weak修饰的变量为何会被加入自动释放池?详细讲解

__weak修饰的变量属于弱引用,如果没有被注册到 @autoreleasePool 中,创建之后也就会随之销毁,为了延长它的生命周期,必须注册到 @autoreleasePool 中,以延缓释放。

当使用__weak修饰的变量时,通过断点调试,系统调用了objc_loadWeakRetained函数,在这个函数里通过对weak指向对象的 obj->rootTryRetain() 操作。在其中根据对象类型进行retain。

然后将对象添加到底层当前的AutoReleasePoolPage中,以延后释放。

每使用一次__weak对象,运行时系统都会将其指向的原始对象先retain,之后保存到自动释放池中(AutoReleasePoolPage的add()函数)。因此如果大量调用__weak对象,则会重复进行此工作。不仅耗费无意义的性能(重复存储同一对象),还会使内存在短时间内大量增长。

NSLog引发的猜想

为什么使用NSLog函数访问__weak对象,就不会将指向的对象加入到autoreleasepool中呢?

通过调试不难发现,通过NSLog访问__weak对象并不是不触发添加到自动释放池的过程,而是在NSLog自身实现中,包含了自己的autoreleasepool,在调用过程中,__weak指向的对象被retain后,加入到了此内部的autoreleasepool中。

6.__weak 修饰的变量是否是线程安全的?

安全的!首先使用__weak修饰的变量时首先会调用objc_storeWeak函数,在这个函数中, 从中取出__weak修饰的变量所引用的对象,以引用的对象的地址作为key从SideTables(引用计数表)中找到对应的SideTable,这里边有一个自旋锁,就是来保证线程安全的。

相关文章

网友评论

      本文标题:1.3 property 关键字 —— strong、weak

      本文链接:https://www.haomeiwen.com/subject/koownhtx.html