Block捕獲自動變量的值
Block匿名函數(shù)又稱為帶自動變量值的匿名函數(shù), 前面我們已經(jīng)知道了匿名函數(shù), 而帶自動變量值如何理解?
我們知道函數(shù)中可以可能使用的變量如下:
1. 自動變量(局部變量)
2. 函數(shù)的參數(shù)
3. 靜態(tài)局部變量
4. 靜態(tài)全局變量
5. 全局變量
而在函數(shù)的多次執(zhí)行過程中, 能夠傳遞的變量有:
1. 靜態(tài)局部變量
2. 靜態(tài)全局變量
3. 全局變量
通常情況下,我們在OC/C++中可以保持變量值且使用類對象來保持變量,并且能夠多次持有該變量.
然而,Blocks提供了這種類似于類的保存變量值的方式,并且能夠多次持有該變量. 因此使用Block可以不用聲明類也無需使用全局變量/靜態(tài)變量,就能夠滿足這種條件.
如下代碼可以證明:
int main() {
int dmy = 256;
int val = 10;
const char *fmt = "val = %d\n";
void (^blk)(void) = ^ {printf(fmt, val);};
val = 2;
fmt = "These values were changed. val = %d\n";
blk();
return 0;
}
最后結(jié)果運(yùn)行結(jié)果答應(yīng): val = 10.
表示Block中可以截獲所使用的自動變量的值, 即保存自動變量在創(chuàng)建Block的瞬時值. 因為Block保存的是瞬時值, 在執(zhí)行Block語法以后,即使修改了自動變量的值,也不會影響B(tài)lock中捕獲的自動變量在Block運(yùn)行時候的值.
__block變量修飾符
前面,我們了解到Block能在執(zhí)行創(chuàng)建Block語法時,自動變量的瞬時值. 但是一旦截獲該瞬時值, 在Block代碼中是無法修改的, 編譯器會報錯.
int val = 0;
void (^blk)(void) = ^{ val = 1;}; // 編譯時,這里就會報錯.
但是如果被截獲的自動變量是OC類對象,也是無法修改該對象的值, 但是能夠操作該對象的成員變量.
id array = [[NSMutableArray alloc] init];
void (^blk)(void) = ^{
[array appendObject:@(1)];// 不會報錯,可以正常運(yùn)行
array = [[NSMutableArray alloc] init]; // 會報錯, 無法修改自動變量的值
};
OC提供__block修飾符,用于修飾自動變量,此時我們就能在Block語法中修改被截獲的自動變量值.
__block int val = 0;
void (^blk)(void) = ^{ val = 1;};// 因為val是__block修飾,因此可以修改
blk();
printf("val = %d\n", val);
該源碼執(zhí)行的結(jié)果是: val = 1. 對于對象類型的變量,也能使用__block進(jìn)行修飾
__block id array = [[NSMutableArray alloc] init];
void (^blk)(void) = ^{
[array appendObject:@(1)];// 不會報錯,可以正常運(yùn)行
array = [[NSMutableArray alloc] init]; // 不會報錯, 可以修改__block自動變量的值
};
blk();
注意Block并沒有實現(xiàn)對C預(yù)言數(shù)組的截獲:
const char text[] = "hello";
void (^blk)(void) = ^ {
printf("%c\n", text[2]); //這里會出錯, 因為Block并沒有實現(xiàn)對C預(yù)言數(shù)組的截獲
}
我們一般使用指針來規(guī)避該問題:
const char *text = "hello";
void (^blk)(void) = ^ {
printf("%c\n", text[2]); //Block可以截獲指針變量(但是無法截獲C語言數(shù)組變量)
}
參考資料
- <<Objective-C 高級編程: iOS與OSX多線程和內(nèi)存管理>>