86437913 2019-06-29
本系列文章主要是对《Objective-C 高级编程》这本书做的读书笔记总结,除了这本书中的内容以外,也加上了自己对开发技术的理解和一些个人的经验分享。
通过 clang(LLVM编译器)命令转换:
clang -rewrite-objc 源代码的文件名
先来看一个最简单的block
int main() { void (^blk)(void) = ^{ printf("我是block\n"); }; blk(); return 0; }
将以上源代码转换成C++源代码
struct __block_impl { void *isa; int Flags; int Reserved; void *FuncPtr; }; //block结构体 struct __main_block_impl_0 { struct __block_impl impl; struct __main_block_desc_0* Desc; //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; } }; // 将来被调用的block内部的代码:block值被转换为C的函数代码 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)}; //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; }
看下面的代码
^ { printf("我是block\n"); };
可以看到,变换后的源代码中也含有相同的表达式
static void __main_block_func_0(struct __main_block_impl_0 * __cself) { printf("我是block\n"); }
通过block使用的匿名函数实际上被作为简单的C语言函数来处理。根据block语法所属的函数名(此处为main)和该block语法在该函数出现的顺序值(此处为0)来给函数命名 __main_block_func_0
该函数的参数 __cself 相当于C++实例方法中指向实例自身的变量this,或是Object-C实例方法中指向对象自身的变量self,即参数 __cself 为指向block值的变量。
这里的 __main_block_func_0 函数并没有使用到 __cself。使用 __cself 的例子将在后面介绍,我们先来看看该参数的声明,参数 __cself 是 __main_block_func_0 结构体的指针。
struct __main_block_impl_0 * __cself
该结构体的声明如下:
struct __main_block_impl_0 { struct __block_impl impl; struct __main_block_desc_0 * Desc; }
第一个成员变量是 impl,我们先来看一下 __block_impl 结构体的声明。
struct __block_impl { void *isa; int Flags; // 标志 int Reserved; void *FuncPtr; }
接下来我们看一下第二个成员变量 Desc指针
struct __main_block_desc_0 { unsigned long reserved; // 版本升级所需的区域 unsigned long Block_size; // block的大小 }
那么,我们再来看一下初始化这些结构体的 _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; }
NSConcreteStackBlock用于初始化 __block_impl结构体 的isa成员,后面将会讲解。我们先来看一下该构造函数的调用。
void (*blk)(void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA));
因为转换较多,所以看起来比较不清楚,我们来去掉转换的部分,再看一下
struct __main_block_impl_0 temp = __main_block_impl_0(__main_block_func_0, &__main_block_desc_0_DATA); struct __main_block_impl_0 *blk = &temp;
这样就容易理解了。该源代码将 __main_block_impl_0结构体 类型的自动变量,赋值给 __main_block_impl_0结构体指针类型的变量blk
下面就来看看 __main_block_impl_0结构体实例构造参数。
__main_block_impl_0 (__main_block_func_0, &__main_block_desc_0_DATA);
第一个参数是由block语法转换为C语言函数指针。第二个参数是作为静态全局变量初始化的 __main_block_desc_0 结构体的实例指针。
以下为 __main_block_desc_0 结构体实例的初始化部分代码,可以看到源代码使用 __main_block_impl_0 结构体的实例大小,进行初始化。
static struct __main_block_desc_0 __main_block_desc_0_DATA = { 0, sizeof(struct __main_block_impl_0) };
下面看看栈上的 __main_block_impl_0结构体 实例是如何根本这些参数进行初始化的。
struct __main_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;
将 __main_block_func_0 函数指针赋给成员变量 FuncPtr
blk();
转换后的源代码如下
((void (*)(struct __block_impl *))((struct __block_impl *)blk)->FuncPtr)((struct __block_impl *)blk);
去掉转换部分:
(*blk -> impl.FuncPtr)(blk);
这就是简单地使用函数指针调用函数。