Block的实质
Block我想写OC的都不会陌生,在项目中我们经常会使用block作为数据处理后的回调,例如通知主线程UI更新等。Block的语法看上去很特别,但是它实际上是作为及普通的C源代码来处理的。含有Block的源代码转换为一般的C语言代码,之后作为C语言代码被编译。
这里插句题外话,很多动态语言底层都是C或者C++,例如Python,ruby底层都是C,这两门语言里面也有block的概念,只不过在python里称为lambda。所以他们的实现方式是相似的。
OK,回到正题。首先,我们通过clang -rewrite-objc filename(clang是编译指令) 将含有block的源文件转换为C源代码,OC源代码如下:
int main() {
//block实现
void (^test)(void) = ^{
printf("Hello World\n");
};
test();
return 0;
}
代码很简单,只是打印一个字符串.但是转换后却又10000+的代码,但是和block相关的代码却在底部,只有几十行而已,我们来看下:
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;
}
};
<br />
static void __main_block_func_0(struct __main_block_impl_0 *__cself){
printf("Hello World!\n");
}
<br />
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)};
<br />
int main() {
void (*test)(void) = (void (*)())&__main_block_impl_0((void *)__main_block_func_0,
&__main_block_desc_0_DATA);
((void (*)(__block_impl *))((__block_impl *)test)->FuncPtr)
((__block_impl *)test);
return 0;
}
上面四段代码就是一个简单Block的C版本,下面我们一一讲解下。
static void __main_block_func_0(struct __main_block_impl_0 *__cself){
printf("Hello World!\n");
}
这段代码代表的就是block的实现,其中__main_block_func_0代表的是主函数中第0个函数,其参数是Block本身,类似C++的this指针和OC的self.
下面我们来看看cself的类型:__main_block_impl_0
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, intflags=0){
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
可以看到这个类型是个结构体,里面有两个结构体参数,外加一个构造方法.其中变量impl是另一个结构体,我们来看下它的内容:
struct __block_impl{
void *isa;
int Flags;
int Reserved;
void *FuncPtr;
}
除了耳熟能详的的isa指针外,我们还看到flags,估计是某些标志;reserved,升级预留区域,以及一个函数指针.
再看看Desc的结构:
struct __main_block_desc_0{
unsigned long reserved;
unsigned long Block_size;
}
代表的是版本升级预留区域,和block大小.
下面我们看看其构造函数的调用:
void (*test)(void) = (void (*)())&__main_block_impl_0((void
*)__main_block_func_0, &__main_block_desc_0_DATA);
}
其实就是将__main_block_impl_0结构体赋值给test变量,顺带初始化其他属性.
之后,调用:
((void (*)(__block_impl *))((__block_impl *)test)->FuncPtr)
((__block_impl *)test);
}
这其实就是一个简单的函数指针调用函数,类似(*name)(paramters..)调用.
致此,block的实质就讲的差不多了,漏了一个_NSConcreteStackBlock下次再讲吧.