block的本质
Block语法实际上是作为极普通的C语言代码来处理的。通过支持Block的编译器,含有Block语法的源代码转换为一般C语言编译器能够处理的源代码,并作为极为普通的C语言源代码被编译。
这只不过是概念的问题,在实际编译时无法转换成我们能够理解的源码,但clang具有转换成我们可读源代码的功能。通过“-rewrite-objc”选项就能将含有block语法的源代码转换为C++的源代码。说是C++,其实尽是使用了struct结构,其本质是C语言的源代码。
clang -rewrite-objc 源代码文件名
下面,我们转换Block语法。
int main(int argc, const char * argv[]) {
@autoreleasepool {
void (^blk)(void) = ^{
printf("Block\n");};
blk();
}
return 0;
}
该源代码通过clang可变换为以下形式:
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\n");}
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 (*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;
}
static struct IMAGE_INFO {
unsigned version; unsigned flag; } _OBJC_IMAGE_INFO = {
0, 2 };
下面,我们将源代码分成几个部分逐步理解。
1. 最初源代码中的Block语法。
^{
printf("Block\n");};
变换后的源代码。
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
printf("Block\n");
}
如变换后的源代码所示,通过Blocks使用的匿名函数实际上被作为简单的C语言函数来处理。另外,根据Block语法所属的函数名和该Block语法在该函数出现的顺序值来给经clang变换的函数命名。
该函数参数__cself相当于C++实例方法中指向实例自身的变量this,或是Objective-C实例方法中指向对象自身的变量self,及参__cself为指向Block值的变量。
2. 参数声明。
struct __main_block_impl_0 {
struct __block_impl impl;
struct __main_block_desc_0* Desc;
}
我们先除去构造函数,__main_block_impl_0结构体其实非常简单。第一个成员变量是impl,其__block_impl结构体的声明如下。
struct __block_impl {
void *isa;
int Flags;
int Reserved;
void *FuncPtr;
};
我们从其名称中可以联想到某些标志、今后升级所需的区域及函数指针。这些会在后面详细说明。第二个成员变量是Desc指针,以下为其__main_block_desc_0结构体说明。
struct __main_block_desc_0 {
unsigned long reserved;
unsigned long Block_size;
};
这些也如同其成员名称所示,其结构体为今后版本升级所需的区域和Block的大小。
3. 初始化这些结构体的__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;
}
我们先看看构造函数的调用。
void (*blk)(void) = (void(*)(void))&__mian_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_0_DATA);
struct __main_block_impl_0 *blk = &tmp;
这样就容易理解了。该源代码将__main_block_impl_0结构体类型的自动变量,即栈上生成的__main_block_impl_0结构体实例的指针,赋值给__main_blcok_impl_0结构体指针类型的变量blk。以下为这部分对应的最初源代码。
void (^blk)(void) = ^{
printf("Block\n");};
将Block语法生成的Block赋给Block类型变量blk。它等同于将__main_block_impl_0结构体实例的指针赋给变量blk。该源代码中的Block就是__main_block_impl_0结构体类型的自动变量,即栈上生成的__main_block_impl_0结构体实例。
4. 下面是__main_block_impl_0结构体实例构造参数。
__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA)
__main_block_func_0 由Block语法转换的C语言函数指针。
&__main_block_desc_0_DATA 作为静态全局变量初始化的__main_block_desc_0_DATA结构体实例指针。
以下为__main_block_desc_0结构体实例的初始化部分代码。
static struct __main_block_desc_0 __main_block_desc_0_DATA = {
0, sizeof(struct __main_block_impl_0)};
由此可知,该源代码使用__main_block_impl_0结构体实例的大小进行初始化。
5. 栈上的__main_block_impl_0结构体实例根据参数初始化。
如果展开__main_block_impl_0结构体的__block_impl结构体,可记述为如下形式:
struct __mian_block_impl_0 {
void *isa;
int Flags;
int Reserved;
void *FuncPtr;
struct __main_block_desc_0* Desc;
}
该结构体根据构造函数会像下面这样初始化。
isa = &_NSConcreteStackBlock;
Flags = 0;
Reserved = 0;
FuncPtr = __main_block_func_0;
Desc = &__main_block_desc_0_DATA;
6. 使用Block的部分。
blk()
这部分可变换为以下代码:
((void (*)(__block_impl *))((__block_impl *)blk)->FuncPtr)((__block_impl *)blk);
去掉转换部分。
(*blk->impl.FuncPtr)(blk);
这就是简单的使用指针调用函数。
7. _NSConcerteStackBlock
isa = &_NSConcerteStackBlock;
将Block指针赋给Block的结构体成员变量isa。其实Block就是Objective-C对象。
id
"id"这一变量类型用于储存Objectivec-C对象。在Objective-C源码中虽然可以像使用void *类型那样随意使用id,但此id类型也能够在C语言中声明。
typedef struct objc_object {
Class isa;
} *id;
id为objc_object结构体的指针类型。
Class
Class为objc_class结构体的指针类型。objc_class结构体声明如下:
struct objc_class {
Class isa;
};
objc_object结构体和objc_class结构体归根结底是各个对象和类的实现中使用的最基本的结构体。
编写简单的类声明来确认。
@interface MyObject : NSObject
{
int val10;
int val11;
}
@end
基于objc_object结构体,该类的对象的结构体如下:
struct NSObject_IMPL {
Class isa;
}