1 基本说明
Block一直是OC的一个重点、难点、黑科技。Block在日常项目中经常使用,他的实现方式和一般的oc代码不一样。同时也很容易出现使用不慎的情况。我们知道OC的本质是C语言+runtime。runtime中的具体实现完全就是汇编加上C语言。而且我们发现大部分都是通过结构体实现的。我们可以通过clang -rewrite-objc main.m这种命令把包含Block的main.m函数反编译(注意:这里所说的反编译并不是真正的反编译,只是把OC源码转换为对等的C++源码)为为C++的具体实现。下面我就会通过这个命令来分析一下Block转换以后的源码。下面所有列子中转换前的代码都在main.m中,替换后的代码都在mainX.cpp中.
2 void (*block)(void)类型解析
我们先看一下转换以前的代码。顶一个一个block。只定义一个变量i并且赋值为1。对应的文件为mainX.cpp。
int main() {
void (^blk)(void) = ^(){
int i = 1;
};
blk();
return 0;
}
下面是转换以后的代码。
struct __block_impl {
void *isa;
int Flags;
int Reserved;
void *FuncPtr;
};
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
int i = 1;
}
struct __main_block_impl_0 {
struct __block_impl impl;
struct __main_block_desc_0* Desc;
__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int flags=0) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
static struct __main_block_desc_0 {
size_t reserved;
size_t Block_size;
} __main_block_desc_0_DATA = { 0, sizeof(struct __main_block_impl_0)};
int main() {
void (*blk)(void) = ( (void (*)()) & __main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA) );
((void (*)(__block_impl *))((__block_impl *)blk)->FuncPtr)((__block_impl *)blk);
return 0;
}
上面是我截取的关键部分源码,其他还有很多辅助性的代码,这里我们就不用管了。看这段代码我们发现一个简单的block的转换成C++以后增加了很多代码:
main函数入口,我们可以发现这个函数里面主要初始化了一个__main_block_impl_0的结构体。并且调用结构体的FuncPtr方法。
__main_block_impl_0结构体。这个结构体有一个__block_impl和__main_block_desc_0结构体。以及一个初始化函数。
__block_impl结构体。这个结构体有四个变量。其中我们可以发现有两个很关键的isa和FuncPtr。
__main_block_desc_0结构体。这个结构体包含了两个size_t类型的属性,主要用于记录Block的内存大小。
__main_block_func_0静态方法,我们发现这个方法就是BLock的具体实现。
2.1具体流程分析
首先main函数代码。
int main() {
void (*blk)(void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA));
((void (*)(__block_impl *))((__block_impl *)blk)->FuncPtr)((__block_impl *)blk);
return 0;
}
我们把它具体简化以后如下:
struct __main_block_impl_0 tmp = __main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA);
struct __main_block_impl_0 *blk = &tmp;
(*blk->impl.FuncPtr)(blk);
1首先初始化一个__main_block_impl_0结构体变量。并且传入的参数是__main_block_func_0指向结构体的具体实现。__main_block_desc_0_DATA是__main_block_desc_0结构体的一个变量,主要目的是指定结构体的大小。
2 把tmp的地址赋值给blk指针。
3 通过blk找到他的impl属性,然后再通过impl这个__block_impl变量获取FuncPtr函数的地址。然后传入blk的指针作为参数。从而实现OC中的blk();这句话。
接下来我们看一下__main_block_impl_0这个结构体。
struct __main_block_impl_0 {
struct __block_