前言
在阅读该篇文章前,推荐阅读
ios - block原理解读(一)
ios - block原理解读(二)
本文解决问题
- __block原理
- copy函数,从栈copy到堆的过程(同对象变量)
__block原理
通过__block修饰的自动变量,可以在block内部进行值修改。
typedef void (^Block)(void);
Block block;
int main(int argc, char * argv[]) {
@autoreleasepool {
__block int a = 10;
block = ^{
a++;
};
return 0;
}
}
将代码编译成C++源码
// 原代码
__block int a = 10;
// c++源码
__attribute__((__blocks__(byref))) __Block_byref_a_0 a = {
(void*)0,
(__Block_byref_a_0 *)&a,
0,
sizeof(__Block_byref_a_0),
10
};
可以看到 变量a 变成了 结构体类型__Block_byref_a_0
下面再看看结构体__Block_byref_a_0
的构造
struct __Block_byref_a_0 {
void *__isa;
__Block_byref_a_0 *__forwarding;
int __flags;
int __size;
int a;
};
通过上面结构体的初始化和结构体的构造,
可以获得以下信息:
- __forwarding存放的是自己本身的地址
- 结构体内的a变量存放的是外部变量a的值
主结构体__main_block_impl_0
的变化
首先说明一点,
在block初始化过程中,有一个由栈block指向堆block的过程。
一开始,栈空间的block有一个__Block_byref_a_0
结构体,
指向外部__Block_byref_a_0
的地址,
其中它的__forwarding指针指向自身,
当block从栈copy到堆时,
堆空间的block有一个__Block_byref_a_0
结构体,
指向外部__Block_byref_a_0
的地址,
其中它的__forwarding指针指向自身(复读机)
如何从栈指向堆,并建立联系呢?
apple源码,如图:
copy函数相关源码.png
copy->forwarding = copy;
就是将堆结构体的__forwarding指针指向自身
src->forwarding = copy;
就是将栈结构体的__forwarding指针指向堆结构体
这样,苹果工程师在背后悄悄地将block copy到了堆上,
而且栈上的block从未被我们利用过。
在看看block入口静态函数
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
__Block_byref_a_0 *a = __cself->a; // bound by ref
(a->__forwarding->a)++;
}
通过当前栈空间主结构体上的__Block_byref_a_0
结构体指针,访问指向堆空间的__forwarding
成员,并获取堆空间上变量的值。
当然,不仅__block修饰的变量会这样,前文的对象类型变量同样会在copy函数内部被转化成类似的结构体进行处理。
网友评论