探尋Block的本質(2)—— 底層結構

Block傳送門🦋🦋🦋

探尋Block的本質(1)—— 基本認識c++

探尋Block的本質(3)—— 基礎類型的變量捕獲markdown

探尋Block的本質(4)—— Block的類型數據結構

探尋Block的本質(5)—— 對象類型的變量捕獲iphone

探尋Block的本質(6)—— __block的深刻分析函數

上一篇,咱們初步認識了Block的一些基本知識。如今,咱們來一塊兒挖掘一下Block的底層結構。 首先仍是新建一個命令行項目
定義一個最最簡單的blockpost

#import <Foundation/Foundation.h>

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        
        //Block的定義
        void (^block)(void) = ^(){
            NSLog(@"I am a block!");
        };
        
        //Block的調用
        block();
        
    }
    return 0;
}
複製代碼

緊接着,經過xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc main.m -o main.cpp命令拿到編譯後的c++文件,添加到項目中。直接查看該文件的尾部 在main函數裏面,有不少強制類型轉換代碼,爲了便於理解,去掉這些轉換代碼(不影響原有的邏輯),將其main函數簡化成以下ui

//【1.2】
struct __main_block_impl_0 {
    
  struct __block_impl impl;
  struct __main_block_desc_0* Desc;
    
    //C++語法下,下面的函數是一個類的構造方法,函數名與類名相同,與oc裏面的init方法相似,返回結構對象
  __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;
  }
};

//【1.1】這個函數是block內部封裝的代碼塊執行邏輯
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {

            NSLog((NSString *)&__NSConstantStringImpl__var_folders_7__p19yp82j0xd2m_1k8fpr77z40000gn_T_main_65b8ed_mi_0);
        }

//【1.3】
static struct __main_block_desc_0 {
  size_t reserved;//保留變量,暫無用處
  size_t Block_size;//block的大小
}__main_block_desc_0_DATA = { 0, sizeof(struct __main_block_impl_0)};

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

        //【1】-------------- 【1.4】
        void (*block)(void) = &__main_block_impl_0(
                                                   __main_block_func_0,
                                                   &__main_block_desc_0_DATA));

        //【2】
        block->FuncPtr(block);
    }
    return 0;
}
複製代碼

下面來分析一下系統底層的執行的邏輯。 首先,根據oc代碼中main 函數裏面的第一句代碼spa

//Block的定義
        void (^block)(void) = ^(){
            NSLog(@"I am a block!");
        };
複製代碼

系統會進行步驟【1.1】,也就是會吧Block中的代碼塊封裝到函數static void __main_block_func_0(struct __main_block_impl_0 *__cself){}裏面.命令行

接下來的步驟【1.2】,系統會定義結構體struct __main_block_impl_0做爲Block的底層數據結構,因爲是C++語法下的結構體,所以這個結構體裏面能夠定義函數,代碼中的函數爲這個結構體的構造函數__main_block_impl_0()3d

接下來的【1.3】步驟,系統又定義告終構體static struct __main_block_desc_0 ,而且生成了一個實例__main_block_desc_0_DATA,用來存放Block的相關描述信息。

最後是【1.4】步驟,系統利用構造函數__main_block_impl_0()生成的對象的地址賦值給block指針。傳入的參數有兩個,分別爲 (1)__main_block_func_0

  • 最後賦值給了__main_block_impl_0->impl->funcPtr

(2)&__main_block_desc_0_DATA

  • 根據結構體static struct __main_block_desc_0 構造

這樣步驟【1】就結束了。再上一幅圖來呈現一下整個過程步驟【1】流程圖

接下來根據根據咱們的OC代碼

//【2】
        block->FuncPtr(block);
複製代碼

系統會經過步驟【2】完成了block的調用,也就是調用block內部封裝的那個函數指針。 block->FuncPtr(block);

⚠️小細節 block->FuncPtr(block);爲何沒報錯?由於按照咱們的代碼結構,應該是block->impl->FuncPtr(block);纔對。 且看struct __main_block_impl_0 中的第一個成員爲struct __block_impl impl;,說明impl 是一個結構體,不是指針,因此能夠直接把它的內容搬到struct __main_block_impl_0 來理解所以block->FuncPtr(block); 等價於block->impl->FuncPtr(block);,兩種方式實際上均可以拿到FuncPtr進行調用。

至此,Block的底層最簡單數據結構就初步分析完了。

Block傳送門🦋🦋🦋

探尋Block的本質(1)—— 基本認識

探尋Block的本質(3)—— 基礎類型的變量捕獲

探尋Block的本質(4)—— Block的類型

探尋Block的本質(5)—— 對象類型的變量捕獲

探尋Block的本質(6)—— __block的深刻分析

相關文章
相關標籤/搜索