block本質(zhì)上也是一個OC對象,它內(nèi)部也有一個isa指針违寞,block是封裝了函數(shù)調(diào)用以及函數(shù)調(diào)用環(huán)境的OC對象。
Block的底層結(jié)構(gòu)
如下圖所示:
先在項目中定義一個block正塌,然后執(zhí)行block茴晋,通過命令行clang -rewrite-objc main.m
,將OC代碼轉(zhuǎn)化為c++代碼扁掸,看一下block的底層結(jié)構(gòu)翘县。
現(xiàn)在通過右側(cè)的OC代碼最域,對照看一下c++源碼。
void(^block)(void) = ^{
NSLog(@"Hello, World!");
};
block();
//定義block
void(*block)(void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA));
//執(zhí)行block內(nèi)部代碼
((void (*)(__block_impl *))((__block_impl *)block)->FuncPtr)((__block_impl *)block);
block就相當于__main_block_impl_0
锈麸;執(zhí)行block镀脂,就相當于__block_impl->FuncPtr
,找到block中封裝的函數(shù)FuncPtr忘伞,然后執(zhí)行薄翅。
Block的變量捕獲
為了保證block內(nèi)部能夠正常訪問內(nèi)部的變量,block有個變量捕獲機制
auto變量存放在動態(tài)存儲區(qū)氓奈,隨著生命周期的結(jié)束而立即釋放翘魄。只有函數(shù)的局部變量才能定義為auto類型。
static變量存放在靜態(tài)存儲區(qū)探颈,在程序整個運行期間都不釋放熟丸。在函數(shù)內(nèi)定義的靜態(tài)變量為靜態(tài)局部變量,在函數(shù)外定義的靜態(tài)變量為靜態(tài)全局變量伪节。
以下分別為Block中引用auto類型局部變量光羞,static局部變量以及全局變量,通過OC代碼編譯成c++源碼的對照
運行結(jié)果:
Block[1635:48861] age is 10, height is 20, weight is 110
上圖可以看出age和height變量都被捕獲到block中怀大,age是值捕獲纱兑,height是值捕獲,全部變量weight沒有被捕獲化借。
對象類型的auto變量
當block內(nèi)部訪問了對象類型的auto變量時
如果block是在棧上潜慎,將不會對auto變量產(chǎn)生強引用
如果block被拷貝到堆上
會調(diào)用block內(nèi)部的copy函數(shù)
copy函數(shù)內(nèi)部會調(diào)用_Block_object_assign函數(shù)
_Block_object_assign函數(shù)會根據(jù)auto變量的修飾符(__strong、__weak蓖康、__unsafe_unretained)做出相應的操作铐炫,形成強引用(retain)或者弱引用如果block從堆上移除
會調(diào)用block內(nèi)部的dispose函數(shù)
dispose函數(shù)內(nèi)部會調(diào)用_Block_object_dispose函數(shù)
_Block_object_dispose函數(shù)會自動釋放引用的auto變量(release)