首先说一下循环引用, 为什么没用__weak
修饰就直接用 self.
属性不会造成循环引用.
循环引用是指两个或者多个对象循环持有造成的无法释放.
类 Person
有个属性 block
, 在 block
实现后, 此时 self
持有 block
, 如果在 block
中, 直接使用 self
, block
将持有 self
, 造成循环引用, 如果 block
本身不是 self
的属性, 则 self
不持有 block
, 即使在 block
中直接使用 self
也不会造成循环引用, 但是为了避免多个对象的循环引用, 所以 block
中最好还是用__weak
, 防止这种情况出现. 代理用 weak
与此同理.
__block:
使外部变量可以在 Block
内部进行修改.
输出:
num:2, pointer:0xb000000000000022
blockNum:3, pointer:0xb000000000000032
可见 __ block 会拷贝原来对象, __block修饰的对象可被 Block 内外同时修改.
![](https://img.haomeiwen.com/i2131368/2547ebf5885ccbef.png)
可见
doctor
和 self
被 block
持有, 即使没有使用__ block
修饰,但他们的属性依然可以被修改, 不会像上面的 num = @3;
那样报错禁止修改, 和 NSArray
是一样的, NSArray
本身是不可修改的, 但是 NSArray 里面存放的对象依然是可以修改的.
__weak:
![](https://img.haomeiwen.com/i2131368/ed78fc9023d519ec.png)
![](https://img.haomeiwen.com/i2131368/b1c3b8f30a523dae.png)
weak
的实现, 将当前对象(= 右边的对象)值的地址存入 weak hash
表, hash
表中的 key
是 weak
对象的地址, value
是当前对象(= 右边的对象)值的地址, 当每次访问weak
对象的时候, 首先找到 key
, 再找到 value
. 当当前对象的值被释放, weak hash
表将 value
设置为nil
. 如果理解不了, 可以看一下 C语言的指针, 指针的指针.
__weak 的作用: weak
表中的指向的对象, 不计入引用计数. 不持有对象, 但可以引用对象, 不参与对象释放.
![](https://img.haomeiwen.com/i2131368/188aa742ee71c5f7.png)
![](https://img.haomeiwen.com/i2131368/252e85de5c25330e.png)
可见被__weak
修饰过的对象, 不会被 block
持有,所以在对象释放的时候, block
中的 weak
对象也被置为 nil
.
但有一种特殊的情况, 就是存放在栈区的对象并不会因为引用对象的释放而释放.
![](https://img.haomeiwen.com/i2131368/5cb84159fe6dd19a.png)
![](https://img.haomeiwen.com/i2131368/b23a175a0bfa213a.png)
字符串常量是存在栈区的, 栈内存并不会动态释放, 而是当当前线程执行完毕后, 释放当前线程的栈内存. 所有的常量都存在栈区.
所以上面的例子中即使使用__ weak
修饰, 但是@"aa"
这个常量并没有被释放, 所以 weak
的地址指向依然存在值.
__strong:
在某个块内对当前对象引用计数加一.
在 AFN 中经常出现 block
外面 __ weak
, 在内部再 __strong
.
这里有个例子需要理解
![](https://img.haomeiwen.com/i2131368/98d02c315523d276.png)
![](https://img.haomeiwen.com/i2131368/0c334ec2de2f09c3.png)
在这个例子中, 在 block
里面已经通过__strong
对对象引用计数加一,但为什么置为 doctor
置为 nil
, 就被释放掉了那?
再看下面的例子:
![](https://img.haomeiwen.com/i2131368/ed9d6587642ae69e.png)
![](https://img.haomeiwen.com/i2131368/3bb88da42b5adcf6.png)
这个例子为什么只有 doctor
是空那?
结合两个例子, 引用计数的改变是一个动态改变, 只有被强引用之后, 引用计数才会加一。
分析例 1:
在 doctor
置为 nil
之前, block
的__ strong
并没有执行, 所以当时 doctor
对象被当前的区块持有, 当 doctor
置为 nil
时, 该对象已经被释放, 所以 __strong
的时候, weak
地址的内存已经被释放, strong 指向 nil, 所以 doctor 对象引用计数并没有加 1.
分析例 2:
在 doctor
被置为nil
之前执行__ strong
, doctor 引用计数加 1, 对象被 block
持有, 执行 doctor = nil
之后, doctor 仍然被 block
持有.
说一下外面__weak
, 里面__strong
的好处, 在被block
引用的时候,不会被 block 持有, 在block
执行完毕, 立即将对象释放, 并不会造成循环引用, 而且还可以在多线程中, 在 block
区块内对对象持有.
这里有个概念就是区块持有和对象持有.
{
// 区块持有
// 在块内声明的变量, 在块执行完毕立即被释放.
}
对象持有: 地址指向内存.
网友评论