Objective-C之Blocks(二)

前言

Objective-C之Blocks(一)中,說(shuō)明了Block的一些用法和特性。其中講到Block的三種特性:

  • 截獲自動(dòng)變量
  • __block說(shuō)明符
  • 截獲的自動(dòng)變量

但是我們還是不知道Block的實(shí)質(zhì)和這些特性是如何實(shí)現(xiàn)的。本文將介紹Block的本質(zhì)。

Block本質(zhì)

想要了解Block的本質(zhì),我們就需要利用clang(LLVM編譯器)將代碼轉(zhuǎn)換為C++代碼閱讀。我們先在終端中進(jìn)入main.m文件路徑,在終端中輸入

clang -rewrite-objc main.m(文件名字)

轉(zhuǎn)換后的文件變得很長(zhǎng),是因?yàn)榫幾g器對(duì)頭文件的處理,我們可以忽略不看,直接在文件里面搜索int main,我們可以看到轉(zhuǎn)換過(guò)后的main函數(shù),和Block的實(shí)現(xiàn)。

int main函數(shù)

分析main函數(shù),我們可以看到一個(gè)Block類(lèi)型變量,其值是一個(gè)__main_block_impl_0結(jié)構(gòu)體,在調(diào)用該結(jié)構(gòu)體構(gòu)造函數(shù)的時(shí)候,傳入了兩個(gè)參數(shù)__main_block_func_0函數(shù)指針和__main_block_desc_0_DATA結(jié)構(gòu)體。

__main_block_func_0函數(shù)

觀察__main_block_func_0函數(shù)

__main_block_func_0函數(shù)

在函數(shù)中參數(shù):__cself和OC中的self相同。
觀察函數(shù)我們發(fā)現(xiàn),其中有NSLog。由此我們可以推測(cè)出,__main_block_func_0函數(shù)是我們自己定義的函數(shù)主體。我們將Block中的代碼塊更改成for循環(huán)來(lái)驗(yàn)證我們的猜想。

main函數(shù)

轉(zhuǎn)換后的__main_block_func_0函數(shù)

轉(zhuǎn)換后的`__main_block_func_0`函數(shù)

猜想正確,所以,__main_block_func_0函數(shù)代表了Block中,我們自己定義的代碼塊。Block通過(guò)將此函數(shù)指針傳遞給__main_block_impl_0結(jié)構(gòu)體指針來(lái)實(shí)現(xiàn)調(diào)用代碼塊。

__main_block_desc_0_DATA結(jié)構(gòu)體

觀察__main_block_desc_0_DATA結(jié)構(gòu)體

__main_block_desc_0_DATA函數(shù)

其中有2個(gè)成員變量,一個(gè)是reserved,它代表今后版本升級(jí)所需要的區(qū)域;還有一個(gè)是Block_size,它代表Block大小。而B(niǎo)lock的大小是__main_block_impl_0結(jié)構(gòu)體的大小。

__main_block_impl_0結(jié)構(gòu)體

觀察__main_block_impl_0結(jié)構(gòu)體

__main_block_impl_0結(jié)構(gòu)體

該結(jié)構(gòu)體中寫(xiě)入了其構(gòu)造函數(shù),所以看起來(lái)比較復(fù)雜。去掉構(gòu)造函數(shù)后,其聲明如下:

struct __main_block_impl_0 {
  struct __block_impl impl;
  struct __main_block_desc_0* Desc;
};

第一個(gè)成員變量是__block_impl結(jié)構(gòu)體;第二個(gè)成員變量是__main_block_desc_0結(jié)構(gòu)體指針,此結(jié)構(gòu)體前文已經(jīng)說(shuō)明過(guò),不再贅述。
關(guān)于__main_block_impl_0結(jié)構(gòu)體的構(gòu)造函數(shù),傳入的三個(gè)參數(shù):fp、desc、flags分別代表函數(shù)指針(此函數(shù)指針即為我們自已定義的代碼塊)、__main_block_desc_0結(jié)構(gòu)體指針、一個(gè)標(biāo)志位(一般為0)

__block_impl結(jié)構(gòu)體

下面我們著重來(lái)看__block_impl結(jié)構(gòu)體,在cpp文件中搜索__block_impl

__block_impl結(jié)構(gòu)體

此結(jié)構(gòu)體中有4個(gè)成員變量:

  • isa指針 : 指向一個(gè)類(lèi)對(duì)象
  • Flags : 某種標(biāo)志
  • Reserved : 今后版本升級(jí)所需的區(qū)域
  • FuncPtr : 函數(shù)指針,指向我們自己的代碼塊

了解了__block_impl結(jié)構(gòu)體,所以我們可以將__main_block_impl_0結(jié)構(gòu)體寫(xiě)成如下形式:

struct __main_block_impl_0 {
  void *isa;
  int Flags;
  int Reserved;
  void *FuncPtr;
  struct __main_block_desc_0* Desc;
};

該結(jié)構(gòu)體根據(jù)構(gòu)造函數(shù)會(huì)像下面這樣初始化:

isa = &_NSConcreateStackBlck;
Flags = 0;
Reserved = 0;
FuncPtr = __main_block_func_0;
Desc = &__main_block_desc_0_DATA;

其中_NSConcreateStackBlck代表一個(gè)對(duì)象,具體的在下一部分中講解。

Block的使用

Block的使用為:

blk();

可轉(zhuǎn)換為以下形式:

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

去掉轉(zhuǎn)換部分:

(*blk->impl.FuncPtr)(blk);

這是簡(jiǎn)單的使用函數(shù)指針調(diào)用函數(shù),傳遞的參數(shù)為block本身,也證明的前文所述__cself和OC中的self相同。

總結(jié)

  • 在我們創(chuàng)建Block的時(shí)候,會(huì)生成__main_block_impl_0結(jié)構(gòu)體變量賦值給Block變量。由于該結(jié)構(gòu)體中存在isa指針,所以使block成為了OC對(duì)象,即該結(jié)構(gòu)體相當(dāng)于基于objc_object結(jié)構(gòu)體的OC類(lèi)對(duì)象結(jié)構(gòu)體。(關(guān)于isa指針請(qǐng)參見(jiàn):關(guān)于oc運(yùn)行時(shí) isa指針詳解)我們以__main_block_func_0函數(shù)指針(其指向我們自定義的代碼塊所在函數(shù))和__main_block_desc_0_DATA結(jié)構(gòu)體(其保存了今后升級(jí)所需區(qū)域和Block大小)來(lái)初始化__main_block_impl_0結(jié)構(gòu)體。通過(guò)過(guò)函數(shù)指針的調(diào)用,我們就實(shí)現(xiàn)了Block的使用。
    * 本文重點(diǎn)講述Block的本質(zhì),關(guān)于Block的特性,請(qǐng)參見(jiàn)以后的文章。
  • 本文重點(diǎn)講述Block的本質(zhì),關(guān)于Block的特性,請(qǐng)參見(jiàn)Objective-C之Blocks(三)
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容