[toc]
參考
OC代碼
int main(int argc, const char * argv[]) {
@autoreleasepool {
NSInteger val = 10;
NSLog(@"1_val = %ld - %p", val, &val);
void (^block)(void) = ^{
NSLog(@"2_val = %ld - %p", val, &val);
};
NSLog(@"3_val = %ld - %p", val, &val);
block();
NSLog(@"4_val = %ld - %p", val, &val);
}
return 0;
}
MRC 輸出:
1_val = 10 - 0x7ffeefbff408
3_val = 10 - 0x7ffeefbff408
2_val = 10 - 0x7ffeefbff3f8 // 包內(nèi): 新的棧地址
4_val = 10 - 0x7ffeefbff408
ARC 輸出:
1_val = 10 - 0x7ffeefbff408
3_val = 10 - 0x7ffeefbff408
2_val = 10 - 0x103a34b30 // 包內(nèi): 堆地址
4_val = 10 - 0x7ffeefbff408
結(jié)論★:
block 訪問(wèn)未被 __block 修飾的非靜態(tài)局部變量:
-
block 包外, 無(wú)論MRC/ARC, 在block定義前后, 局部變量的地址未改變, 始終在棧上梁棠。
- 這種情況下, block并不會(huì)改變包外變量的地址。
-
block 包內(nèi), 局部變量的瞬時(shí)值被捕獲為block結(jié)構(gòu)體的成員, 生成一個(gè)全新的變量, 擁有新的地址竖共。
MRC下, block仍然在棧上, 所以局部變量也在棧上;
ARC下, block因被強(qiáng)指針引用, 會(huì)被copy到堆, 所以局部變量也隨block拷貝到了堆;
☆ 變量被copy到堆, 并不是__block的作用竹伸。
注意:
若未使用
__block
, block中做如下修改val = 2;
, 編譯會(huì)報(bào)錯(cuò) "Variable is not assignable (missing __block type specifier)
” , 即, 不能修改指針指向旱易。
如何證明 "block內(nèi)部” 打印的是堆地址?
把三個(gè)16進(jìn)制的內(nèi)存地址轉(zhuǎn)成10進(jìn)制就是:
定義前:6171559672 6171041512
包內(nèi)部:5732708296 6174180216
定義后:5732708296
中間相差 438851376 個(gè)字節(jié), 也就是 418.5M 的空間, 因?yàn)槎训刂芬∮跅5刂? 又因?yàn)閕OS中一個(gè)進(jìn)程的棧區(qū)內(nèi)存只有1M, Mac也只有8M, 顯然a已經(jīng)是在堆區(qū)了岖瑰。
C++代碼
MRC/ARC下, 編譯的C++代碼一致:
int main(int argc, const char * argv[]) {
{ __AtAutoreleasePool __autoreleasepool;
NSInteger val = 10; // 局部變量
NSLog((NSString *)&__NSConstantStringImpl__var_folders_p5_mp3284bs2xb073r91w__n99r0000gn_T_main_d3fa5d_mi_0, val, &val);
void (*block)(void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, val)); // 注意第3個(gè)參數(shù) val
NSLog((NSString *)&__NSConstantStringImpl__var_folders_p5_mp3284bs2xb073r91w__n99r0000gn_T_main_d3fa5d_mi_2, val, &val);
((void (*)(__block_impl *))((__block_impl *)block)->FuncPtr)((__block_impl *)block);
NSLog((NSString *)&__NSConstantStringImpl__var_folders_p5_mp3284bs2xb073r91w__n99r0000gn_T_main_d3fa5d_mi_3, val, &val);
}
return 0;
}
// block 的結(jié)構(gòu)體
struct __main_block_impl_0 {
struct __block_impl impl;
struct __main_block_desc_0* Desc;
NSInteger val; // 捕獲到的局部變量
// 結(jié)構(gòu)體構(gòu)造函數(shù)
__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, NSInteger _val, int flags=0) : val(_val) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
// __main_block_impl_0 初始化的第1個(gè)參數(shù), 作為函數(shù)指針傳遞
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
NSInteger val = __cself->val; // bound by copy
NSLog((NSString *)&__NSConstantStringImpl__var_folders_p5_mp3284bs2xb073r91w__n99r0000gn_T_main_d3fa5d_mi_1, val, &val);
}
// __main_block_impl_0 初始化的第2個(gè)參數(shù)
// 注意, 未使用 __block修飾基本數(shù)據(jù)類型的局部變量, desc結(jié)構(gòu)體中沒(méi)有 copy 和 dispoose 函數(shù)
static struct __main_block_desc_0 {
size_t reserved;
size_t Block_size;
} __main_block_desc_0_DATA = { 0, sizeof(struct __main_block_impl_0) };
block 被轉(zhuǎn)換成了一個(gè)
__main_block_impl_0
的結(jié)構(gòu)體實(shí)例, 其中, 結(jié)構(gòu)體__main_block_impl_0
的成員包含了捕獲到的局部變量val狮杨。在結(jié)構(gòu)體
__main_block_impl_0
的構(gòu)造方法中, 局部變量val作為第3個(gè)入?yún)? 被捕獲半沽。執(zhí)行block時(shí), 通過(guò) block 找到
__main_block_func_0
, 并把當(dāng)前block作為參數(shù)傳遞到__main_block_func_0
函數(shù)中身诺。