相關文章
- (一)Block的實質(zhì)初探
- (二)Block之存儲域 NSConcreteStackBlock,NSConcreteGlobalBlock春畔,NSConcreteMallocBlock
- (三)Block之截獲變量和對象
- (四)Block之 __block修飾符及其存儲域
我們已經(jīng)知道脱货,Block將被轉(zhuǎn)換為Block的結(jié)構(gòu)體的自動變量, 即棧上生成的結(jié)構(gòu)體實例律姨。而且當Block被當做OC對象來看時振峻,Block的isa指針為_NSConcreteStackBlock
導讀
本文以下內(nèi)容我們將探究包括_NSConcreteStackBlock
的與之相似的幾個類:
- _NSConcreteStackBlock
- _NSConcreteGlobalBlock
- _NSConcreteMallocBlock
_NSConcreteStackBlock
中包stack
關鍵字,我們可以推測它設置在棧上(編譯器分配內(nèi)存)
_NSConcreteGlobalBlock
中包Global
關鍵字择份,我們可以推測它設置在數(shù)據(jù)區(qū)域
_NSConcreteMallocBlock
中包Malloc
關鍵字扣孟,我們可以推測它設置在堆上(由程序員分配內(nèi)存)
那么在分配內(nèi)存上對應區(qū)域:
類 | 設置對象的存儲于 |
---|---|
_NSConcreteStackBlock | 棧 |
_NSConcreteGlobalBlock | 數(shù)據(jù)區(qū) |
_NSConcreteMallocBlock | 堆 |
_NSConcreteStackBlock
int i = 0;
void (^blk)() = ^() {
printf("%d", i);
};
blk();
局部定義的Block 經(jīng)過代碼轉(zhuǎn)換后
struct __main_block_impl_0 {
struct __block_impl impl;
struct __main_block_desc_0* Desc;
__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int flags=0) {
impl.isa = &_NSConcreteStackBlock; //設置在棧區(qū)的Block
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
大多數(shù)時候,clang轉(zhuǎn)換的源代碼通常是_NSConcreteStackBlock
對象
_NSConcreteGlobalBlock
void (^blk) () = ^{
printf("Block");
};
int main(int argc, const char * argv[]) {
@autoreleasepool {
blk();
}
return 0;
}
全局定義的Block經(jīng)過代碼轉(zhuǎn)換后
struct __blk_block_impl_0 {
struct __block_impl impl;
struct __blk_block_desc_0* Desc;
__blk_block_impl_0(void *fp, struct __blk_block_desc_0 *desc, int flags=0) {
impl.isa = &_NSConcreteGlobalBlock; //設置在數(shù)據(jù)區(qū)的Block
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
當以下兩種情況時荣赶,Block被轉(zhuǎn)換為_NSConcreteGlobalBlock
- 全局變量有Block語法的時候
全局變量的地方不能使用自動變量凤价,不存在對自動變量進行捕獲的情況,所以Block不依賴執(zhí)行時候的狀態(tài)拔创,這時候Block設置為_NSConcreteGlobalBlock
- Block語法不使用截獲的自動變量時候
int(^blk)(int count) = ^(int count) {
return count;
};
示例代碼如上利诺,Block并不需要捕獲自動變量,也可以將Block設置在數(shù)據(jù)區(qū)域
_NSConcreteMallocBlock
對于以下代碼
typedef int (^Block)(int num);
Block foo(){
int i = 0;
return ^{printf("blk:%d", i);};
}
將如上代碼轉(zhuǎn)換為匯編輸出(如何獲取匯編代碼輸出請看 iOS 獲取匯編輸出方法 以下采用整理過后的偽代碼形式)
Block foo(){
int i = 0;
Block tmp = &__foo_block_impl_0(
&__foo_block_func_0,
&__foo_block_desc_0_DATA,
i));
tmp = objc_retainBlock(tmp);
return objc_autoreleaseReturnValue(tmp);
}
如下為objc_retainBlock
的源碼
id objc_retainBlock(id x) {
return (id)_Block_copy(x);
}
很顯然調(diào)用了objc_retainBlock
就等于調(diào)用了_Block_copy
, 作用是將Block拷貝至堆伏蚊。
那么立轧,當以下情況時,Block被拷貝至堆躏吊,那么Block也將配置為_NSConcreteMallocBlock
- Block作為返回值氛改,超出了變量作用域
補充
Block作為返回值時,編譯器會自動將變量拷貝至堆比伏,有時候編譯器無法判斷胜卤,需要手動調(diào)用copy方法,將Block拷貝至堆
typedef void (^Block)();
id foo(){
int i = 0;
return [[NSArray alloc] initWithObjects:
^{printf("blk:%d", i);},
nil];
}
NSArray *arr = foo();
Block blk = (Block)[arr firstObject];
blk();//此處執(zhí)行報錯
該段代碼執(zhí)行將會報錯赁项,因為foo()
執(zhí)行結(jié)束后葛躏,棧上的Block就被釋放了,所以需要手動copy 下Block
id foo(){
int i = 0;
return [[NSArray alloc] initWithObjects:
[^{printf("blk:%d", i);} copy],//手動copy block到堆
nil];
}
最后附上各類Block Copy后執(zhí)行動作
類 | 設置對象的存儲于 | Copy動作執(zhí)行后 |
---|---|---|
_NSConcreteStackBlock | 棧 | 棧復制到堆 |
_NSConcreteGlobalBlock | 數(shù)據(jù)區(qū) | 不做變化 |
_NSConcreteMallocBlock | 堆 | 引用計數(shù)增加 |