从大二的开始接触oc就用到了weak属性修饰词,但是当时只是知道如何去用这个关键字:防止循环引用。根本没有深入地去了解它。
在刚来北京的时候面试过程中也常常考到该知识点。大点的公司可能会问它如何使用?如何在对象销毁后将对象置nil,小点的公司可能只问一下它的使用。
now,如果你对它产生恐惧或者曾经对它产生过恐惧(+1),如果你被该关键字弄得整天吃不下饭,睡不着觉,那么可以继续往下阅读,希望读过该博客之后能够帮到你。
废话不多说,开始介绍。
先来看看最简单的一个例子:
我们可以看到此时输出的结果为:
2017-02-07 13:20:41.119278 test[7341:2187477] result is :(null)
如果我们使用的strong来修饰weakpoint,此时输出的结果为:
2017-02-07 13:23:13.211164 test[7344:2187993] result is :<uilabel: 0x100206070; frame = (0 0; 0 0); userinteractionenabled = no; layer = <_uilabellayer: 0x17009a590>>
如果我们使用assign来修饰weakpoint,此时运行程序可能会崩溃(因为如果引用操作发生时内存还没有改变内容,依旧可以输出正确结果,如果引用的时候内存内容发生改变了,就会crash),因为当assign指针所指向的内存被释放之后,不会自动赋值为nil,这样再次引用该指针的时候就会导致野指针操作。
对上述代码运行结果进行分析:
当使用weak关键字的时候,不会增加对象的计数,而且当所指对象置nil的时候,使用weak修饰的指针将被赋值为nil;
当使用strong关键字的时候,会增加对象的计数,也就是说会保持对象值的存在,所以当使用strong的时候weakpoint还会有值。
因此,我们从这里可以得出一个结果:
strong是强引用,它会保持对象值的存在;
weak是弱引用,当weak指针指向的对象摧毁之后,属性值也会清空(nil out)。
(注意:使用 _ _ weak修饰 和在@ property里面设置weak是一样的)
但是当我们执行如下代码的时候:
你会发现只有yourstring为空,其他两个都不为空,这个是为什么呢?原因如下
这里是因为字符字面值永远不会被释放,所以你的weak指针还是指向它。
那么请问weak指针指向对象被回收的时候该指针是如何被自动置为nil的呢??
首先,大家可以看一下博客最后面的附录,里面有两个文档,严格来说是apple的opensouce。里面有一个objc-weak的类。这里是一个objc-weak.h类和一个objc-weak.mm类。
从.h中可以看到以下几个关键的两个结构体:weak_entry_t和weak_table_t,以及一些方法。接下来简单介绍一下weak如何自动置为nil。
以下是从nsobject.mm里面摘出的一些方法:
该function的作用是初始化一个新的weak指针指向对象的地址。其中的参数介绍如下:
location段:_ _ weak指针的地址
newobj:对象的指针地址
这里调用的storeweak方法,storeweak方法里面通过template模板的参数进行更新weak操作,看源码可以知道里面会调用weak_register_no_lock/weak_unregister_no_lock等objc-weak.mm里面的方法进行相应的操作。objc-weak.h里面有句话:
对象被废弃时最后调用objc_clear_deallocating。该函数实现如下:
也就是调用了cleardeallocating,继续追踪可以发现,它最终是使用了迭代器来取weak表的value,然后调用weak_clear_no_lock,然后查找对应的value,将该weak指针置空:
objc_clear_deallocating该函数的动作如下:
从weak表中获取废弃对象的地址为键值的记录
将包含在记录中的所有附有 _ _ weak修饰符变量的地址,赋值为nil
将weak表中删除该记录
从引用计数表中删除废弃对象的地址为键值的记录
看了objc-weak.mm的源码大概了解了:其实weak表示一个hash表,然后里面的key是指向对象的地址,value是weak指针的地址的数组。
以上便是我个人对weak的理解,查看objc4的源码,发现里面更多的都是结构体、结构体套结构体等等。