- block 介绍
- 截获变量
- __block修饰符
- Block的内存管理
- Block的循环引用
截获变量
先看一个问题
|
|
带着问题往下看,有这几种类型的变量
局部变量 – 基本数据类型 , 对象类型 (对于基本数据类型的局部变量截获其值,对于对象类型的局部变量连同 所有权修饰符 一起截获)
静态局部变量 ( 以指针形式截获局部静态变量 )
全局变量 (不截获全局变量)
- 静态全局变量 (不截获静态全局变量)
从这段代码来深入了解一下
|
|
使用 clang命令看一下编译后的源码 MCBlock.cpp
看这一段
- 可以看到,局部变量截获的就是它的值
- 静态局部变量以指针形式截取的
- 对象类型的类型连同其修饰符一起截获,理解这个就能更好的理解 Block 循环引用的问题,后续会说
- 全局和静态全局变量不截获
然后回到问题
|
|
__block修饰符
什么情况下需要用到 __block修饰符 呢?
对被截获变量进行赋值操作的时候 (区分 赋值 和使用)
看一些笔试题
|
|
总结下,对变量进行赋值的时候,下面这些不需要__block修饰符
- 静态局部变量
- 全局变量
- 静态全局变量
|
|
加了 __block 修饰之后,这个变量就变成了一个对象
在 multiplier 进行赋值的时候
forwarding指向原来的对象, 通过 forwarding 指针进行赋值,修改掉 multiplier 的值
Block的内存管理
Block类型
- _NSConcreteGlobalBlock 全局
- _NSConcreteStackBlock 栈类型
- _NSConcreteMallocBlock 堆类型
看一下各个类型的Block在内存上面的分配
###Block的copy操作
比如现在声明一个成员变量Block,而在栈上创建这个Block去赋值,如果没有对Block进行Copy操作的话,当我们通过成员变量去访问这个Block的时候,可能会因为栈对应的函数退出之后在内存当中就销毁掉了,继续访问就会引起内存崩溃
Block的循环引用
下面这段代码就会造成循环引用
|
|
self 持有 Block,而 Block 里有成员变量 array, 持有 self,所以就造成了循环引用,怎么解决呢?
|
|
为什么用_ weak 修饰符解决循环引用? 这个其实在截获变量里有讲过,截获对象的时候会连同修饰符一起截获,在外部定义的如果是 weak 修饰符,在 Block 里所产生的结构体里面所持有的成员变量也是 _weak 类型
再看一段代码,这样写有什么问题?
|
|
这样在 ARC 模式下是会产生循环引用,引起内存泄漏的
__block修饰后的指向是原来的对象,会造成循环引用
怎么解决呢,首先想到的当然是断开其中一个环
|
|
在调用完 blockSelf 后将它置为nil,断开其中的一个环,就可以让内存得到释放和销毁
但是这样会有一个弊端,如果长期不调用这个block,这个循环引用的环就会一直存在
Block 面试总结
- 什么是 Block?
- 为什么 Block会产生循环引用?
- 如何理解 Block 截获变量的特性?
- 你都遇到过哪些循环引用?怎么解决的?
转载请标明出处