Block 是 C 語言擴展,本質是一個結構體指針,內部包含:
類似其他語言的閉包:可訪問定義作用域內的局部變量。
// 1. 定義類型(可選,簡化語法)
typedef void(^MyBlock)(int, int);
// 2. 定義並賦值(捕獲外部變量)
int factor = 10;
MyBlock block = ^(int a, int b) {
printf("Result: %d\n", (a + b) * factor); // 捕獲 factor
};
// 3. 執行
block(3, 4); // 輸出 70
^返回類型(參數){體} 即時定義。
@property (nonatomic, copy) void(^completionBlock)(BOOL success);
原因:Block 創建在棧上,離開作用域即銷毀。copy 將其從棧拷貝到堆,延長生命周期。ARC 下自動處理,MRC 必須手動 [block copy]/release。
// 錯誤示例
self.block = ^{
[self doSomething]; // Block 強引用 self
};
// self 強引用 block (屬性 copy) → 循環
__weak typeof(self) weakSelf = self;
self.block = ^{
__strong typeof(weakSelf) strongSelf = weakSelf;
if (strongSelf) {
[strongSelf doSomething];
}
};
__weak:不增加引用計數,對象釋放自動置 nil。__strong:在 Block 內部臨時持有,防止執行過程中對象被釋放。
dispatch_async(queue, ^{
[self doSomething]; // 直接用 self 安全
});
核心原因:GCD Block 執行完即刻釋放,不會被長期持有。dispatch_async 內部不持有 Block 的強引用(僅在隊列執行期間臨時持有),self 也不持有 Block。無「雙向強引用」,無循環。
NSBlockOperation *op = [NSBlockOperation blockOperationWithBlock:^{
[self doSomething]; // 潛在循環
}];
[queue addOperation:op];
但通常安全:Operation 執行完成後自動從隊列移除並釋放,打破循環。長時間運行或取消操作時需注意,可在 Block 內用 weakSelf。
| 變量類型 | 捕獲方式 | 可修改性 |
|---|---|---|
| 局部基本類型 | 值捕獲(複製) | 不可修改(除非加 __block) |
| 局部對象指針 | 引用捕獲(retain) | 可修改指向對象內容 |
| static/全局變量 | 直接訪問地址 | 可修改 |
__block 修飾 | 引用捕獲(共享存儲) | 可修改變量本身 |
__block id obj = [[MyClass alloc] init];
// Block 內修改 obj 指向新對象
block = ^{
obj = [MyClass new]; // 合法,共享存儲
};
ARC 下 __block 對對象指針會 retain,Block 拷貝到堆時會 retain 捕獲的 __block 變量指向的對象。
copy。self 必做 weakSelf(除 GCD 短時任務)。__block。