对于学习过Objective-C的人来说,一定知道OC中神奇的代码块block
,下面我们就来讲一讲block
。
Block实际上是作为极普通的C语言源码来处理的:含有Block语法的源码首先被转换成C语言编译器能处理的源码,再作为普通的C源代码进行编译。
使用LLVM编译器的clang命令可将含有Block的Objective-C代码转换成C++的源代码,以探查其具体实现方式:
clang -rewrite-objc 源码文件名
我们在Xcode中创建一个Command Line Tool项目,语言选择Objective-C,代码如下:
#import <Foundation/Foundation.h>
int (^blk)(int) = ^(int count){
return count+1;
};
int main(int argc, const char * argv[]) {
@autoreleasepool {
^{};
blk(1);
}
return 0;
}
经过clang转化后我们会得到一个main.cpp的c++文件打开后发现,这个文件代码有9万多行,前面都是Foundation
框架的代码,我们不用去在意,将文件移到最后就能看到我们所需要的代码了。
struct __blk_block_impl_0 {
struct __block_impl impl;
struct __blk_block_desc_0* Desc;
__blk_block_impl_0(void *fp, struct __blk_block_desc_0 *desc, int flags=0) {
impl.isa = &_NSConcreteGlobalBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
static int __blk_block_func_0(struct __blk_block_impl_0 *__cself, int count) {
return count+1;
}
static struct __blk_block_desc_0 {
size_t reserved;
size_t Block_size;
} __blk_block_desc_0_DATA = { 0, sizeof(struct __blk_block_impl_0)};
static __blk_block_impl_0 __global_blk_block_impl_0((void *)__blk_block_func_0, &__blk_block_desc_0_DATA);
int (*blk)(int) = ((int (*)(int))&__global_blk_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, 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) {
}
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(int argc, const char * argv[]) {
/* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool;
((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA));
((int (*)(__block_impl *, int))((__block_impl *)blk)->FuncPtr)((__block_impl *)blk, 1);
}
return 0;
}
我们还需要 Foundation
框架中block结构体的定义
struct __block_impl {
void *isa;
int Flags;
int Reserved;
void *FuncPtr;
};
isa
:指向block的类的指针:block一共有三个类
- _NSConcreteStackBlock:在栈上创建的Block对象
- _NSConcreteMallocBlock:在堆上创建的Block对象
- _NSConcreteGlobalBlock:全局数据区的Block对象
FuncPtr
:指向block实现方法的指针
我们先来看main函数中的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;
}
};
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
}
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)};
__main_block_impl_0
是block的完整结构体,
__block_impl
是一个block的结构体
__main_block_desc_0
是一个block描述的结构体其中
reserved
是保留的字节数
Block_size
是block结构体的空间,大小就是sizeof(struct __main_block_impl_0)
其中也有结构体的构造函数
__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;
}
main函数中我们本来写的^{}
转化为了((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA));
一串复杂的代码,其实就是构造了一个__main_block_impl_0
结构体转化成代码就是
__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA,int flag=0) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = __main_block_func_0;
Desc = __main_block_desc_0_DATA;
}
这样就定义了一个完整的__main_block_impl_0
结构体。
下面我们来看下blk
Block的实现。
struct __blk_block_impl_0 {
struct __block_impl impl;
struct __blk_block_desc_0* Desc;
__blk_block_impl_0(void *fp, struct __blk_block_desc_0 *desc, int flags=0) {
impl.isa = &_NSConcreteGlobalBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
static int __blk_block_func_0(struct __blk_block_impl_0 *__cself, int count) {
return count+1;
}
static struct __blk_block_desc_0 {
size_t reserved;
size_t Block_size;
} __blk_block_desc_0_DATA = { 0, sizeof(struct __blk_block_impl_0)};
static __blk_block_impl_0 __global_blk_block_impl_0((void *)__blk_block_func_0, &__blk_block_desc_0_DATA);
int (*blk)(int) = ((int (*)(int))&__global_blk_block_impl_0);
大致和上一个block相同,不过blk是一个全局的block,定义的时候也在main
方法外面,isa
指针的值为&_NSConcreteGlobalBlock
调用blk时代码为
((int (*)(__block_impl *, int))((__block_impl *)blk)->FuncPtr)((__block_impl *)blk, 1);
从中可以看见调用的就是__block_impl
结构体的FuncPtr
是一个void指针,指向的就是static int __blk_block_func_0(struct __blk_block_impl_0 *__cself, int count)
这个静态方法。
到这里,我们就能理解block是如何实现的了。
总结一下,block在c语言中是以结构体的方法存储的,在Objective-C
中是当做对象来使用的,block有三种不同的类型,分别是全局block,栈block和堆block。