基本认识
block本质上是一个封装了函数调用以及函数调用环境的OC对象,内部也包含一个isa指针。
block的底层实现
简单的block底层实现如下:
1 | struct __main_block_impl_0 { |
变量捕获-基础数据类型
auto变量
定义如下一个捕获auto类型变量(离开作用域自动销毁)的block:
1 | int age = 10; |
将其转换为c++代码:
1 | int age = 10; |
block底层结构为:
1 | struct __main_block_impl_0 { |
可见,block在捕获auto变量的方式为值传递。
static变量
定义如下一个捕获static类型变量的block:
1 | static int age = 10; |
将其转换为c++代码:
1 | static int age = 10; |
block底层结构为:
1 | struct __main_block_impl_0 { |
可见,block在捕获static变量的方式为指针传递。
全局变量
定义如下一个捕获全局变量的block:
1 | //age此时被定义为全局变量 |
将其转换为c++代码:
1 | void (*block)(void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA)); |
block底层结构为:
1 | struct __main_block_impl_0 { |
可见,全局变量可以直接使用,不会被block捕获。
总结
变量捕获-对象类型
当block内部访问了对象类型的auto变量时:
- 如果block是在栈上,将不会对auto变量产生强引用
如果block被拷贝到堆上:
- 会调用block内部的copy函数
- copy函数内部会调用_Block_object_assign函数
- _Block_object_assign函数会根据auto变量的修饰符(__strong、__weak、__unsafe_unretained)做出相应的操作,形成强引用(retain)或者弱引用
如果block从堆上移除:
- 会调用block内部的dispose函数
- dispose函数内部会调用_Block_object_dispose函数
- _Block_object_dispose函数会自动释放引用的auto变量(release)
此时block的底层相关代码如下:
1 | static struct __main_block_desc_0 { |
block的类型
blcok有三种类型,都继承自NSBlock。可以通过class方法或者isa指针查看其具体类型。
- __NSGlobalBlock__ (_NSConcreteGlobalBlock) :没有访问auto变量
- __NSStackBlock__ (_NSConcreteStackBlock : 访问了auto变量
- __NSMallocBlock__ (_NSConcreteMallocBlock): __NSStackBlock\调用了copy方法
下面代码的输出结果为:__NSGlobalBlock__
1 | void (^block)(void) = ^(){ |
下面代码ARC环境下的输出结果为:__NSMallocBlock__,MRC的输出结果为:__NSStackBlock__
1 | int age = 10; |
下面代码的输出结果为:__NSStackBlock__
1 | int age = 10; |
三种类型block的内存存储以及每一种类型的block调用copy后的结果如下所示:
__block修饰符
底层实现
__block可以用于解决block内部无法修改auto变量值的问题。__block不能修饰全局变量、静态变量(static)。
1 | __block int age = 10; |
上面的代码会被编译为:
1 | struct __main_block_impl_0 { |
可见,__block修饰的变量会被包装成一个对象。
__block变量的内存管理
当block在栈上时,并不会对__block变量产生强引用。
当block被copy到堆时:
- 会调用block内部的copy函数
- copy函数内部会调用_Block_object_assign函数
- _Block_object_assign函数会对__block变量形成强引用(retain)[仅ARC, MRC不会retain]
当block从堆中移除时:
- 会调用block内部的dispose函数
- dispose函数内部会调用_Block_object_dispose函数
- _Block_object_dispose函数会自动释放引用的__block变量(release)
__forwarding指针
为什么会指向自己呢,原因是为了确保当栈中的Block复制到堆中的时候,在栈中仍然能正确访问堆中的变量。
循环引用
循环引用产生的原因:
解决循环引用:
- ARC: __weak、__unsafe_unretained
- MRC: __block