Block(閉包)是 Objective-C 2.0 引入的核心特性,將可執行代碼與其捕獲的變量環境封裝為一級對象。它廣泛應用於異步回調、集合遍歷、多線程併發、動畫完成塊等場景。本文系統梳理 Block 語法、變量捕獲規則、記憶體模型(棧/堆/全域)、__block 修飾符、循環引用解決方案與最佳實踐模式。
// 完整宣告語法:回傳型別 (^變數名)(參數列表)
void (^blockVoidVoid)(void); // 無參無回傳
int (^blockIntVoid)(void); // 無參有回傳
void (^blockVoidInt)(int); // 有參無回傳
int (^blockIntIntInt)(int, int); // 多參有回傳
// 定義賦值:使用 ^ 運算子
blockVoidVoid = ^(void) {
NSLog(@"執行了 blockVoidVoid");
};
blockIntIntInt = ^(int a, int b) {
NSLog(@"執行了 blockIntIntInt");
return a + b;
};
void (^block)(int a) = ^(int a) {
NSLog(@"%d", a);
};
block(3); // 輸出 3
// 參數
void executeBlock(void (^block)(void)) {
block();
}
// 回傳
int (^createAdder(int base))(int) {
return ^(int x) { return base + x; };
}
Block 定義時「拍攝快照」,捕獲區域變量的當前值(按值複製)。Block 內部無法修改外部變量。
int num = 10;
void (^myBlock)(void) = ^(void) {
NSLog(@"num = %d", num); // 只能讀取,捕獲值 10
// num = 20; // 編譯錯誤
};
myBlock(); // 輸出: num = 10
__block:引用捕獲加上 __block 修飾符,變量改為引用捕獲,Block 內可修改,且修改對外部可見。編譯器會將該變量包裝在一個結構體中,放在堆上管理。
__block int num = 10;
void (^myBlock)(void) = ^(void) {
NSLog(@"num = %d", num); // 10
num = 20; // 允許修改
};
myBlock();
NSLog(@"num = %d", num); // 20,外部也被修改
| 類型 | 儲存位置 | 生命週期 | 觸發條件 | 需手動管理 |
|---|---|---|---|---|
| 全域 Block | 資料區 (.data) | 程式全程 | 不捕獲任何區域變量 | 否 |
| 棧 Block | 堆疊 | 定義作用域結束 | 捕獲區域變量(預設) | 是 (需 copy 升級) |
| 堆 Block | 堆 | 引用計數歸零 | 對棧 Block 執行 copy | 是 (ARC 自動管理) |