1、本质
- 是一个oc对象,它内部有isa指针;继承于NSObject;比如可以通过调用class方法查看block的类型
- Block是封装了函数调用以及函数调用环境的oc对象
2、 block的数据结构
Funcptr为函数调用地址,这里的age为block截取外部的变量
Desc为描述信息结构体,block_size为block的大小
block的调用时候实质是funcptr的实现
3、block的变量捕获
为了保证block内部能正常使用外部变量,block有个捕获机制
- 对于局部基本数据变量捕获是值捕获(作用域的原因,不能保证局部变量什么时候释放);static局部变量是指针捕获,
- 全局变量不会捕获,直接取值;(因为谁就可以访问到)
- Oc对象的捕获连同修饰词一起捕获
4、block三种类型
可以通过block调用class或者isa指针查看block的类型,最终都继承于NSBlock
- 全局区(数据区、data区)block _NSConcreteGlobalBlock : 不捕获外部变量的block(可以使用全局变量,因为只是取值没有捕获)
- 栈区block _NSConcreteStackBlock:捕获外部变量,
- 堆区block _NSConcreteMallocBlock: _NSConcreteStackBlock copy得到
5、block的三种类型的copy操作
- 全局copy什么也不做
- 栈区copy得到堆区block
- 堆区copy 引用引用计数器加一
- 如果block在栈区,将不会对auto变量产生强引用
- 栈区block进行copy生成堆区block的时候,底层会自动调用一个叫_block_object_assign的函数,这个函数会根据auto变量的修饰符(__strong、__weak、__unsafe_unretained)做出操作,类似retain(形成强引用、弱引用)
- 堆区block的移除,block内部执行dispose函数,dispose函数内部会调用_block_object_dispose函数,该函数会自动释放引用的auto变量,类似于release操作
ARC下编译器会根据情况自动将栈block进行copy,以下情况会触发copy
- Block做为函数返回值
- 将block赋给 __strong 指针的(强引用的时候)时候
- 一些foundation框架中的函数block
- GCD 里面的block
6、block修改外部变量、__block
- 全局、局部static 变量可以更改(变量一直存在内存中,不可取)
- 局部临时变量使用__block修饰,(__block不能修饰全局、static变量)
__block修饰变量本质:编译器会将__block变量包装成一个对象,对象结构体内部有被修饰的变量和__foewarding指针;内部也会通过_block_object_assign进行强引用(MRC不会强引用),_block_object_dispose函数进行释放操作;
注意: Block操作可变数组的时候,不需要添加__block,
7、block的循环引用问题
对象持有block 、block持有了对象 ;形成循环引用
解决的方案:
__weak: 对象释放 ,指针置为nil
__unsafe_unretained: 对象释放 ,指针不会置为nil
__block: 要自己将持有对象赋空