1.如何編譯
進入項目目錄中友瘤,執(zhí)行cc -rewrite-objc '目標(biāo)文件'
如果編譯報錯請看文章:Objective-C編譯成C++代碼報錯
2.分析基礎(chǔ)數(shù)據(jù)類型block的編譯結(jié)果
int main(int argc, const char * argv[]) {
int any = 1;
void (^test)() = ^ {
NSLog(@"%d",any);
};
test();
return 0;
}
編譯->看結(jié)果:
編譯的C++代碼會有幾萬行,我們只需要截取我們需要的部分,以下所展示的是截取的結(jié)果
編譯結(jié)果:
即使是精簡的,看上去還是心亂如麻,對吧?不要灰心,下面有溫馨的.
2.1 block(int)_before_copy
溫馨的:
在圖片block(int)_before_copy
中,我們能清晰的看到各個結(jié)構(gòu)體之間的關(guān)系.
以struct __main_block_impl_0
為主導(dǎo):
struct __main_block_impl_0 {
struct __block_impl impl;//內(nèi)含的子結(jié)構(gòu)體(block內(nèi)含的代碼所在)
struct __main_block_desc_0* Desc;//block的描述
int any;//block捕獲了外部變量
};
2.2 block(int)_after_copy
已經(jīng)說過ARC環(huán)境下會將NSStackBlock
類型的block
進行自動copy轉(zhuǎn)換成NSMallocBlock
類型的block
.
在block(int)_after_copy
內(nèi),
step1
:將NSStackBlock
類型的block
copy轉(zhuǎn)換成NSMallocBlock
類型的block
的過程.(具體過程會很復(fù)雜,后面的文章會說)
對照:堆+棧+靜態(tài)三區(qū)的內(nèi)容,不難看出:
struct __main_block_impl_0
被完完整整的拷貝的了一份.
struct __main_block_impl_0
內(nèi)的子元素
所指向的,有的拷貝了一份,有的則維持原來指向.
struct __main_block_impl_0 {
struct __block_impl impl;//完整拷貝
struct __main_block_desc_0* Desc;//指向的靜態(tài)區(qū)元素,不需要拷貝
int any;//完整拷貝
};
到這里,block
應(yīng)用的step1
完成.
2.3 block調(diào)用
關(guān)于block調(diào)用的一句話:"神經(jīng)病"一眼才能看懂,得慢慢看
step2開始,我們將代碼打斷來看
( void (*)(__block_impl *) )//2.3讀地址,強轉(zhuǎn)成函數(shù)
((__block_impl *)test) //2.1C語言式的父子類,子轉(zhuǎn)父
->FuncPtr //2.2獲取函數(shù)指針
)((__block_impl *)test);//2.4調(diào)用函數(shù)
- step2.1:
((__block_impl *)test)
test指針雖然是函數(shù)指針
卻真正的指向了__main_block_impl_0結(jié)構(gòu)體
的實例
__main_block_impl_0結(jié)構(gòu)體
的實例指針被強轉(zhuǎn)成__block_impl結(jié)構(gòu)體
的實例指針.這為什么可行?
struct __block_impl {
void *isa;
int Flags;
int Reserved;
void *FuncPtr;
};
struct __main_block_impl_0 {
struct __block_impl impl;
struct __main_block_desc_0* Desc;
int any;
};
從結(jié)構(gòu)上看__main_block_impl_0結(jié)構(gòu)體
是包含__block_impl結(jié)構(gòu)體
,也就是說__main_block_impl_0結(jié)構(gòu)體
是對__block_impl結(jié)構(gòu)體
的擴充,其實這也就是C語言式的父子關(guān)系:__main_block_impl_0結(jié)構(gòu)體
是子,__block_impl結(jié)構(gòu)體
是父.再精確到內(nèi)存地址上,__block_impl結(jié)構(gòu)體
排在__main_block_impl_0結(jié)構(gòu)體
的開頭,也就是說在有__main_block_impl_0結(jié)構(gòu)體
實例地址的情況下,讀__block_impl結(jié)構(gòu)體
的相應(yīng)大小就得到了__block_impl結(jié)構(gòu)體
的實例.所以這個強轉(zhuǎn)就是子轉(zhuǎn)父的操作
step2.2:
->FuncPtr
以__block_impl結(jié)構(gòu)體
實例獲取FuncPtr,順理成章step2.3:
(void (*)(__block_impl *))
而FuncPtr只是一個泛型指針,要做函數(shù)用,需要將其強轉(zhuǎn)成可以調(diào)用的函數(shù)step2.4:
(__block_impl *)test
再次由test指針強轉(zhuǎn)成__block_impl結(jié)構(gòu)體指針,作為參數(shù)傳入函數(shù)
.
調(diào)用完成,step2結(jié)束.
總結(jié)就是:通過函數(shù)指針test調(diào)用函數(shù)FnucPtr,傳入的參數(shù)為指針test本身,完成調(diào)用
參考文獻:
Block技巧與底層解析 by tripleCC