《Objective-C 高级编程》第二篇:Block源代码解析

本系列文章主要是对《Objective-C 高级编程》这本书作的读书笔记总结,除了这本书中的内容之外,也加上了本身对开发技术的理解和一些我的的经验分享。编程

1、Objective-C源代码 转 C++源代码的的方法

经过 clang(LLVM编译器)命令转换:函数

clang -rewrite-objc 源代码的文件名

先来看一个最简单的blockthis

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;
}

看下面的代码code

^ {
    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 结构体的指针。it

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);

这就是简单地使用函数指针调用函数。

相关文章
相关标签/搜索