Block的实质:
#import <Foundation/Foundation.h> int main() { void(^blk)(void)=^{printf("Block");}; blk(); return 0; }
使用clang将该block转换。
clang -rewrite-objc 源代码文件
转换后形成的block文件是:
struct __block_impl { void *isa; int Flags; int Reserved; void *FuncPtr; };
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 void __main_block_func_0(struct __main_block_impl_0 *__cself) {
printf("Block");}
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;
}
下面我们就来具体分析一下这些源代码:
首先我们看到
^{printf("Block");};
转换后称为
static void __main_block_func_0(struct __main_block_impl_0 *__cself) { printf("Block");}
如上所示的,block使用的匿名函数,其实被转换成c语言函数处理,,该函数中 的_cself是_main_block_func_0结构体的指针。
struct __main_block_impl_0 { struct __block_impl impl; struct __main_block_desc_0* Desc;
具有两个成员变量,_block_impld 代码如下:
struct __block_impl { void *isa; int Flags; int Reserved; void *FuncPtr; };
第二个成员变量是
__main_block_desc_0:其代码如下:
static struct __main_block_desc_0 {
size_t reserved;
size_t Block_size;
}
这个包含了今后版本升级所需的区域以及block的大小。
下面我们来看一下初始化含有这些数据的结构体的代码:
__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; }
在这里面NSConcreteStackBlock用来初始化,接下来我们对构造函数的调用:
void(*blk)(void)=((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA));
因为转换太多实在太难以理解,所以我们去掉转换的部分,这样得到的结果是:
struct _main_block_impl_0 tmp=_main_block_impl_0(_main_block_func_0,&_main_block_desc_DATA); struct _main_block_impl_0 *blk=&tmp;
这样一来我们可以清楚的看到,
void(^blk)(void)=^{printf("Block");};
这个语句的实现了_main_block_impl_0的两个变量,一个功能函数,一个desc,将block语法生成的block类型对象再赋值给blk,相当于将实例的指针赋值给blk,_main_block_desc_DATA主要是对_main_block_impl_0的结构体实例的大小进行初始化.
blk();
这部分代码转换成为了
((void (*)(__block_impl *))((__block_impl *)blk)->FuncPtr)((__block_impl *)blk);
我们一样去掉转换的部分得到:
(*blk)->impl.Funcptr(blk)
这其实是最简单的函数指针调用函数,正如我们刚才确认的那样,在调用函数中我们看出block是作为参数进行传递的。
那么到底什么是NSConcreteStackBlock呢?
为了理解他首先我们需要理解oc类和对象的实质,其实,所谓的block就是对象,“id”这一变量用于存储oc对象,NSConcreteStackBlock就相当于c结构体实例,将block作为oc对象处理时,该类的信息就放置于NSConcreteStackBlock处。