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

這就是簡單地使用函數指針調用函數。

相關文章
相關標籤/搜索