iOS:Block的本質

我們項目中經常使用block來進行回調傳值,之前我對block的認識也就僅僅的停留在基礎的層面,包括簡單的使用和一些基本的避免循環引用的方法,這篇博客是我在對block進行了更深一層的學習之後的記錄和總結,希望對大家有所幫助。

Block的本質

新建一個命令行項目,寫一個簡單的block如下面所示。

void (^myBlock)(void) = ^{
    NSLog(@"11");
};        
myBlock();

使用clang工具把main.m文件編譯爲cpp文件。main函數變成了下面這個樣子

int main(int argc, const char * argv[]) {
    /* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool; 

        void (*myBlock)(void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA));

        ((void (*)(__block_impl *))((__block_impl *)myBlock)->FuncPtr)((__block_impl *)myBlock);


    }
    return 0;
}

裏面有很多類型轉換的代碼,但是不難看出,我們的block被編譯成了一個__main_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;
  }
};

這個__main_block_impl_0結構體有兩個屬性,一個是__block_impl類型的結構體,一個是指向__main_block_desc_0結構體的指針。我們一個一個的看。

//__block_impl 結構體
struct __block_impl {
  void *isa;
  int Flags;
  int Reserved;
  void *FuncPtr;
};

我們可以看到這個結構體有一個isa指針,同時在__main_block_impl_0這個結構體中是直接包含__block_impl結構體,從內存結構中其實這個isa指針就在__main_block_impl_0裏面,也就是說block被編譯成了一個擁有isa指針的結構體。瞭解過isa的應該知道,這算是oc對象的一個標誌了,那也就是說block其實也是一個oc的對象。

我們繼續看__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;
  }

這是一個初始化的方法,因爲這是c++代碼嘛,c語言的結構體應該是不可以這樣寫的。

只要是給這個__block_impl類型的impl賦值,這裏我們先主要看這個fp指針。

從編譯之後main函數中看出來,在初始化__main_block_impl_0的時候傳進去的參數是一個__main_block_func_0

// __main_block_func_0
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
    NSLog((NSString *)&__NSConstantStringImpl__var_folders_zx_pr_dx33d329d7xxgk9kgjdw80000gp_T_main_85af40_mi_0);
}

可以看出來這是我們block回調執行的方法,也就是說impl.FuncPtr指向了block的回調。

__main_block_impl_0還有一個Desc參數,我們可以在文件中查一下。

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

reserved是一個保留字段,Block_size就是記錄我們這個block佔內存的大小。

本節小結
Block本質就是一個OC對象,內部有isa指針。
Block是封裝了函數調用和函數調用的環境的OC對象。

我們來個草圖來梳理一下最簡單的block的內部結構。

block圖示.png

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章