于是,用__block修饰的int变量i化身为__Block_byref_i_0结构体的最后一个成员变量。
代码中blk对应的结构体也发生了变化:
[cpp]
struct __main_block_impl_0 {
struct __block_impl impl;
struct __main_block_desc_0* Desc;
__Block_byref_i_0 *i; // by ref
__main_block_impl_0(void*fp, struct__main_block_desc_0 *desc, __Block_byref_i_0 *_i, intflags=0) : i(_i->__forwarding) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
__main_block_impl_0发生的变化就是int类型的成员变量i换成了__Block_byref_i_0 *类型,从名称可以看出现在要通过引用方式来捕获了。
对应的函数也不同了:
[cpp]
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
__Block_byref_i_0 *i = __cself->i; // bound by ref
(i->__forwarding->i) = 0; // 看起来很厉害的样子
printf("%d\n", (i->__forwarding->i));
}
main函数也有了变动:
[cpp]
int main(intargc, constchar * argv[])
{
__block __Block_byref_i_0 i = {(void*)0,(__Block_byref_i_0 *)&i, 0, sizeof(__Block_byref_i_0), 1024};
void(*blk)(void) = (void(*)(void))&__main_block_impl_0((void*)__main_block_func_0, &__main_block_desc_0_DATA, (struct__Block_byref_i_0 *)&i, 570425344);
((void(*)(struct__block_impl *))((struct__block_impl *)blk)->FuncPtr)((struct__block_impl *)blk);
return 0;
}
前两行代码创建了两个关键结构体,特地高亮显示。
这里没有看__main_block_desc_0发生的变化,放到后面讨论。
使用__block类型指示符的本质就是引入了__Block_byref_{$var_name}_{$index}结构体,而被__block关键字修饰的变量就被放到这个结构体中。另外,block结构体通过引入__Block_byref_{$var_name}_{$index}指针类型的成员,得以间接访问到外部变量。
通过这样的设计,我们就可以修改外部作用域的变量了,再一次应了那句话:
There is no problem in computer science that can’t be solved by adding another level of indirection.
指针是我们最经常使用的间接手段,而这里的本质也是通过指针来间接访问,为什么要特地引入__Block_byref_{$var_name}_{$index}结构体,而不是直接使用int *来访问外部变量i呢?
另外,__Block_byref_{$var_name}_{$index}结构体中的__forwarding指针成员有何作用?
请继续往下看 :)
5. 背后的内存管理动作
在Objective-C中,block特性的引入是为了让程序员可以更简洁优雅地编写并发代码(配合看起来像敏感词的GCD)。比较常见的就是将block作为函数参数传递,以供后续回调执行。
先看一段完整的、可执行的代码:
[cpp]
#import <Foundation/Foundation.h>
#include <pthread.h>
typedef void (^DemoBlock)(void);
void test();
void *testBlock(void*blk);
int main(int argc, const char * argv[])
{
printf("Before test()\n");
test();
printf("After test()\n");
sleep(5);
return 0;
}
void test()
{
__block int i = 1024;
void(^blk)(void) = ^{ i = 2048; printf("%d\n", i); };
pthread_tthread;
int ret = pthread_create(&thread, NULL, testBlock, (void*)blk);
printf("thread returns : %d\n", ret);
sleep(3);// 这里睡眠1s的话,程序会崩溃
}