blocks是C語言的擴充功能。blocks是帶有自動變量(局部變量)的匿名函數。
截獲自動變量
int main()
{
int dmy = 256;
int val = 10;
const char *fmt = "val = %d\\n";
void (^blk)(void) = ^{
printf(fmt,val);
};
val = 2;
fmt = "These value were changed. val = %d\\n";
blk();
return 0;
}
結果:val = 2
分析:block語法的表達式使用的是它之前聲明的自動變量fmt和val。block表達式截獲所使用的自動變量的值為瞬間值。因為block表達式保存了自動變量的值(截獲),所以在執行block語法后,即使改寫了block中使用的自動變量的值也不會影響block執行的結果。
需要在block中修改一個變量的值,需要使用\\block說明符。
block的實質
block實際上是作為極普通的C語言源代碼來處理的。通過支持block的編譯器,含有block語法的源代碼轉換為一般C語言編譯器能夠處理的源代碼,并作為極為普通的C語言代碼被編譯。
clang -rewrite-objc sourceFileName
通過這個命令可以將含有block語法的源代碼轉換為C++代碼。
通過觀察,Block轉換為Block的結構體類型的自動變量,\_\block變量轉換為\\_block變量的結構體類型的自動變量(即棧上生成的該結構體的實例)。
表 1-1 Block與\_\_block變量的實質
名稱 | 實質 |
---|---|
Block | 棧上Block的結構體實例 |
__block變量 | 棧上__block變量的結構體實例 |
表 1-2 Block的類
類 | 設置對象的存儲域 |
---|---|
_NSConcreteStackBlock | 棧 |
_NSConcreteGlobalBlock | 程序的數據區域(.data區) |
_NSConcreteMallocBlcok | 堆 |
Block為_NSConcreteGlobalBlock類對象的情況
- 記述全局變量的地方有Block語法時
- Block語法的表達式中不使用應截獲的自動變量時
除了以上兩種情況block語法生成的block為_NSConcreteStackBlock類對象,且設置在棧上。
將block配置在堆上的NSConcreteMallocBlock類在何時使用呢?
block超出變量作用域可存在的原因是?
\\block變量用結構體成員變量\\_forwarding存在的原因是?
Blocks提供了將Block和\_\_block變量從棧上復制到堆上的方法,這樣,即使Block語法記述的變量作用域結束,堆上的Block還可以繼續存在。
那么什么時候棧上的Block會復制到堆呢?
- 調用Block的copy實例方法時
- Block作為函數返回值返回時
- 將Block賦值給附有\_\_strong修飾符、id類型的類或Block類型成員變量時
- 在方法名中含有usingBlock的Cocoa框架方法或Grand Central Dispatch的API中傳遞Block時
堆上的Block被廢棄時會調用dispose函數。
只有調用Block_copy函數才能持有截獲的附有\\_strong修飾符的對象類型的自動變量值。當需要在Block中使用對象類型自動變量時,除以下情形,推薦調用Block的copy方法。
- Block作為函數返回值返回時
- 將Block賦值給附有\_\_strong修飾符、id類型的類或Block類型成員變量時
- 在方法名中含有usingBlock的Cocoa框架方法或Grand Central Dispatch的API中傳遞Block時