前言
在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)糯崎。
分析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ù)
在函數(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)證我們的猜想搪缨。
轉(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)體
其中有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)體
該結(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
此結(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(三)