1.如何編譯
進(jìn)入項(xiàng)目目錄中,執(zhí)行cc -rewrite-objc '目標(biāo)文件'
如果編譯報(bào)錯(cuò)請(qǐng)看文章:Objective-C編譯成C++代碼報(bào)錯(cuò)
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++代碼會(huì)有幾萬(wàn)行,我們只需要截取我們需要的部分,以下所展示的是截取的結(jié)果
編譯結(jié)果:
即使是精簡(jiǎn)的,看上去還是心亂如麻,對(duì)吧?不要灰心,下面有溫馨的.
2.1 block(int)_before_copy
溫馨的:
在圖片block(int)_before_copy
中,我們能清晰的看到各個(gè)結(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)說(shuō)過(guò)ARC環(huán)境下會(huì)將NSStackBlock
類型的block
進(jìn)行自動(dòng)copy轉(zhuǎn)換成NSMallocBlock
類型的block
.
在block(int)_after_copy
內(nèi),
step1
:將NSStackBlock
類型的block
copy轉(zhuǎn)換成NSMallocBlock
類型的block
的過(guò)程.(具體過(guò)程會(huì)很復(fù)雜,后面的文章會(huì)說(shuō))
對(duì)照:堆+棧+靜態(tài)三區(qū)的內(nèi)容,不難看出:
struct __main_block_impl_0
被完完整整的拷貝的了一份.
struct __main_block_impl_0
內(nèi)的子元素
所指向的,有的拷貝了一份,有的則維持原來(lá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開(kāi)始,我們將代碼打斷來(lái)看
( void (*)(__block_impl *) )//2.3讀地址,強(qiáng)轉(zhuǎn)成函數(shù)
((__block_impl *)test) //2.1C語(yǔ)言式的父子類,子轉(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)體
的實(shí)例
__main_block_impl_0結(jié)構(gòu)體
的實(shí)例指針被強(qiáng)轉(zhuǎn)成__block_impl結(jié)構(gòu)體
的實(shí)例指針.這為什么可行?
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)體
,也就是說(shuō)__main_block_impl_0結(jié)構(gòu)體
是對(duì)__block_impl結(jié)構(gòu)體
的擴(kuò)充,其實(shí)這也就是C語(yǔ)言式的父子關(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)體
的開(kāi)頭,也就是說(shuō)在有__main_block_impl_0結(jié)構(gòu)體
實(shí)例地址的情況下,讀__block_impl結(jié)構(gòu)體
的相應(yīng)大小就得到了__block_impl結(jié)構(gòu)體
的實(shí)例.所以這個(gè)強(qiáng)轉(zhuǎn)就是子轉(zhuǎn)父的操作
step2.2:
->FuncPtr
以__block_impl結(jié)構(gòu)體
實(shí)例獲取FuncPtr,順理成章step2.3:
(void (*)(__block_impl *))
而FuncPtr只是一個(gè)泛型指針,要做函數(shù)用,需要將其強(qiáng)轉(zhuǎn)成可以調(diào)用的函數(shù)step2.4:
(__block_impl *)test
再次由test指針強(qiáng)轉(zhuǎn)成__block_impl結(jié)構(gòu)體指針,作為參數(shù)傳入函數(shù)
.
調(diào)用完成,step2結(jié)束.
總結(jié)就是:通過(guò)函數(shù)指針test調(diào)用函數(shù)FnucPtr,傳入的參數(shù)為指針test本身,完成調(diào)用
參考文獻(xiàn):
Block技巧與底層解析 by tripleCC