Block 語法速查
// 完整形式
返回類型 (^名稱)(參數列表) = ^(參數列表) { 實現代碼; };
// 無參無回傳
void (^sayHello)() = ^{ NSLog(@"Hello"); };
// 有參有回傳
int (^add)(int, int) = ^(int x, int y) { return x + y; };
// typedef 簡化複雜簽名
typedef double (^MathBlock)(double, double);
MathBlock multiply = ^(double a, double b) { return a * b; };
變量捕獲規則
- 區域變數:預設值捕獲(唯讀),Block 內看到的是建立時的副本;
- 加
__block 修飾後變為引用捕獲,可在 Block 內修改且修改對外可見;
- 靜態/全域/成員變數:隱含引用捕獲,無需
__block;
- self:Block 內使用 self 會強引用 self,極易形成循環引用。
循環引用與解決方案
// 錯誤:self 強引用 block,block 強引用 self
@property (nonatomic, copy) void (^task)(void);
self.task = ^{ [self doSomething]; };
// 正確:弱引用打破循環
__weak typeof(self) weakSelf = self;
self.task = ^{ [weakSelf doSomething]; };
// 或在 block 內部轉強引用(延長生命週期)
__weak typeof(self) weakSelf = self;
self.task = ^{
typeof(self) strongSelf = weakSelf;
if (strongSelf) [strongSelf doSomething];
};
常見實戰場景
- 集合遍歷:
[array enumerateObjectsUsingBlock:...]
- 動畫:
[UIView animateWithDuration:animations:completion:]
- 排序:
[array sortedArrayUsingComparator:^NSComparisonResult(id a, id b){...}]
- 通知:
[[NSNotificationCenter defaultCenter] addObserverForName:object:queue:usingBlock:]
- 網路回調:
dataTaskWithRequest:completionHandler:^(NSData *, NSURLResponse *, NSError *){...}
- 多線程:
dispatch_async(queue, ^{...})
Block 儲存與複製
- Block 建立在堆疊上,離開作用域即失效;
- 需跨作用域使用時必須
copy 到堆上:@property (copy) void (^block)();
- 加入陣列/字典時自動 copy,但手動管理時請記得
[block copy]。