Block(塊)是蘋果為 C 語言增加的閉包特性,封裝一段可執行代碼,支持參數、返回值、捕獲外部變量。廣泛用於多線程(GCD)、異步回調、集合遍歷/排序、動畫轉場等場景。
// 定義一個 Block 類型別名
typedef int (^MySumBlock)(int, int);
// 定義並賦值 Block 變量
MySumBlock sumBlock = ^(int a, int b) {
return a + b;
};
// 調用
int result = sumBlock(3, 5); // 8
__block 修飾符默認情況下,Block 內訪問外部局部變量為只讀(值捕獲)。若需在 Block 內修改外部變量,必須加 __block。
void testBlockVariableCapture() {
int readOnly = 10;
__block int writable = 20;
void (^printer)(void) = ^{
NSLog(@"readOnly: %d", readOnly); // OK
// readOnly = 15; // 編譯錯誤:只讀
writable = 30; // OK:__block 允許修改
NSLog(@"writable: %d", writable);
};
printer();
NSLog(@"After block, writable = %d", writable); // 30
}
用 Block 替代傳統 Target-Action 或 Delegate,代碼更聚合。
// Button.h
typedef void (^ButtonClickBlock)(id sender);
@interface Button : NSObject
@property (nonatomic, copy) ButtonClickBlock clickHandler; // 必須用 copy
- (void)simulateClick;
@end
// Button.m
@implementation Button
- (void)simulateClick {
if (self.clickHandler) {
self.clickHandler(self); // 回調
}
}
@end
// 使用
Button *btn = [Button new];
btn.clickHandler = ^(id sender) {
NSLog(@"按鈕 %@ 被點擊", sender);
};
[btn simulateClick];
關鍵點:Block 屬性必須用 copy,因為 Block 初始在棧上,copy 會將其複製到堆上,延長生命周期。
| 特性 | Block | 函數指針 |
|---|---|---|
| 語法 | ^(參數){ 體 } | 返回類型 (*名)(參數) |
| 捕獲上下文 | ✅ 自動捕獲外部變量 | ❌ 無法直接捕獲,需傳 void* 上下文 |
| 內聯優化 | ✅ 編譯器可內聯 | ❌ 難以內聯 |
| 內存管理 | 需 copy/release (MRC) 或 ARC 自動管理 | 僅代碼段地址,無生命周期問題 |
Block 捕獲 self 且 self 持有 Block 會導致循環引用。解決:在 Block 內使用 __weak 弱引用。
__weak typeof(self) weakSelf = self;
self.completionBlock = ^{
[weakSelf doSomething]; // 不持有 self
};
Block 是現代 Objective-C 開發的核心工具。掌握 __block、copy、循環引用處理,能寫出更簡潔、安全的異步代碼。