Blocks篇:3.Blocks使用捕獲到的變量
所謂Blocks捕獲變量,即在Block函數(shù)體內(nèi)使用外部聲明的變量。
1. 捕獲局部變量(自動變量)和靜態(tài)局部變量
// main.m
int main(int argc, const char * argv[]) {
@autoreleasepool {
int uselessVal = 1;
// 局部變量
int val = 3;
// 靜態(tài)局部變量
static int staticVal = 4;
void (^myBlock)(void) = ^{
// 捕獲val和staticVal使用
printf("val = %d\n", val);
printf("staticVal = %d\n", staticVal);
};
myBlock();
}
return 0;
}
轉(zhuǎn)換后的C++代碼部分如下:
// main.cpp
/** Block的完整結(jié)構(gòu)體聲明 */
struct __main_block_impl_0 {
struct __block_impl impl;
struct __main_block_desc_0* Desc;
int val; // 內(nèi)部捕獲的局部變量
int *staticVal; // 內(nèi)部捕獲的靜態(tài)局部變量
__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int _val, int *_staticVal, int flags=0) : val(_val), staticVal(_staticVal) {
impl.isa = &_NSConcreteStackBlock; // 這里只按照MRC編譯確定,ARC則不同
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
/** Block中函數(shù)體轉(zhuǎn)換生成的C函數(shù) */
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
// 獲取Block結(jié)構(gòu)體實例中捕獲的變量
int val = __cself->val; // bound by copy
int *staticVal = __cself->staticVal; // bound by copy
printf("val = %d\n", val);
printf("staticVal = %d\n", (*staticVal));
}
// main函數(shù)
int main(int argc, const char * argv[]) {
/* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool;
int uselessVal = 1;
int val = 3;
static int staticVal = 4;
void (*myBlock)(void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, val, &staticVal)); // 初始化時賦值,將要捕獲的變量傳遞進去
((void (*)(__block_impl *))((__block_impl *)myBlock)->FuncPtr)((__block_impl *)myBlock);
}
return 0;
}
以上可以看出:
- 只有Block內(nèi)部使用的變量才會被捕獲;
-
普通的局部變量是通過值傳遞方式進行捕獲,靜態(tài)局部變量是通過指針傳遞方式進行的;
- 原因:
- 由于C函數(shù)的聲明和實現(xiàn)是在原局部變量的生命周期之外(這里是main函數(shù)外部),使用時原變量早已被釋放,故需要直接將值保存到Block結(jié)構(gòu)體實例中;
- 對于靜態(tài)局部變量,由于靜態(tài)變量是存儲在專門的數(shù)據(jù)區(qū)(也就可以理解為全局),在C函數(shù)中也可以直接訪問,故只需將其指針保存在Block結(jié)構(gòu)體實例中即可。
- 原因:
- C函數(shù)內(nèi)部使用時,通過傳遞進去的Block指針取出被捕獲的變量值。
- 補充:在ARC環(huán)境下,對于Block的類型(isa),
- 捕獲普通局部變量后,在運行時,系統(tǒng)會將Block拷貝到堆上,變?yōu)開NSConcreteMallocBlock
- 捕獲靜態(tài)局部變量,雖然使用,實際也只是記錄該變量的指針,Block為_NSConcreteGlobalBlock
- 沒有捕獲任何變量的Block為_NSConcreteGlobalBlock類型
2. 捕獲全局變量和全局靜態(tài)變量
// main.m
int globalVal = 3;
static int globalStaticVal = 4;
int main(int argc, const char * argv[]) {
@autoreleasepool {
void (^myBlock)(void) = ^{
printf("globalVal = %d\n", globalVal);
printf("globalStaticVal = %d\n", globalStaticVal);
};
myBlock();
}
return 0;
}
轉(zhuǎn)換后的C++部分代碼為:
// main.cpp
int globalVal = 3;
static int globalStaticVal = 4;
/** Block完整結(jié)構(gòu)體聲明 */
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;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
// 直接使用
printf("globalVal = %d\n", globalVal);
printf("globalStaticVal = %d\n", globalStaticVal);
}
可以看到,
- 對于全局變量(包含靜態(tài)全局變量),由于在Block的C函數(shù)中可以直接訪問,故無需在Block結(jié)構(gòu)中對其進行額外的保存工作。
- 在ARC環(huán)境下,Block的類型(isa)為_NSConcreteGlobalBlock