block原理探究
最近在做业务时,可谓一步一个坑,所以回过头来补充下最基础的原理知识。
PS:基础好才是真的好!!!
Block 原理探究
将 block 相关的知识点归纳如下:
1 | 1. block 本质 |
Block 本质
测试代码:
1 | int main(int argc, const char * argv[]) { |
请出 clang
命令看一下 block 被编译成了什么:
1 | int main(int argc, const char * argv[]) { |
将上面的类型转换代码删除之后可以得到如下结构:
1 | void (*block)(void) = &__main_block_impl_0(__main_block_func_0, |
可以看到调用 block() 其实就是调用 block->FuncPtr(block)。
再来看看 block 被编译后的具体结构:
1 | struct __main_block_impl_0 { |
由 void (*block)(void) = &__main_block_impl_0(__main_block_func_0, &__main_block_desc_0_DATA));
参照上面 block 被编译的具体结构可以看到,block 被编译成了结构体 struct __main_block_impl_0
,而 struct __main_block_impl_0
由结构体 struct __block_impl impl
和 struct __main_block_desc_0* Desc
构成,再来看下 impl 的结构:
1 | struct __block_impl { |
是不是很熟悉,第一个就是 isa 指针,而最后一个 FuncPtr 指针就是我们调用 block 时所调用的:block->FuncPtr(block)
。
由上分析可以得出 block 调用的流程:
- block 被编译成了结构体
struct __main_block_impl_0
- 结构体初始化时传递了两个参数:
__main_block_func_0
和&__main_block_desc_0_DATA
__main_block_func_0
就是我们要实现的 block 内部的代码(block 内部代码被包装到了这个函数里面)&__main_block_desc_0_DATA
的结构里面有一个 size,看结构体的初始化赋值:sizeof(struct __main_block_impl_0) 可以得出,size 存储的就是整体 block 的大小__main_block_impl_0
结构体初始化时通过内部的同名构造函数将包装好的 block 实现函数__main_block_func_0
指针和结构体__main_block_desc_0_DATA
的地址传给了__main_block_impl_0
相关的值__block_impl
里面的 FuncPtr 存储的就是 block 内存代码被包装的函数指针- block 调用最终就是通过
void (*block)(void)
block 指针调用 FuncPtr 函数:block->FuncPtr(block)
通过 struct __block_impl 里面的 isa 指针可以想到 block 可能是一个 OC 对象,下面我们来验证一下:1
2
3
4
5
6NSLog(@"%@ - %@ - %@ - %@",
[block class],
[[block superclass] class],
[[[block superclass] superclass] class],
[[[[block superclass] superclass] superclass] class]);
输出结果
1 | __NSGlobalBlock__ - __NSGlobalBlock - NSBlock - NSObject |
可以看到,block 最终的 superclass 就是继承自 NSObject,所以 block 就是一个 OC 对象。
综合上得出:
block 就是封装了函数调用和函数调用环境的 OC 对象