performSelectorInBackground:@selector(method:) withObject: **只能傳遞單一對象**,需同時傳遞 UITableViewCell 和 NSString 兩個參數。
- (void)viewDidLoad {
[super viewDidLoad];
NewsCell *cell = ...; // 獲取 cell
NSString *imageName = @"image.png";
// 1. 封裝參數字典
NSDictionary *params = @{
@"cell": cell,
@"imageName": imageName
};
// 2. 後台線程調用包裝方法
[self performSelectorInBackground:@selector(doSomethingWrapper:) withObject:params];
}
- (void)doSomethingWrapper:(NSDictionary *)params {
// 3. 解包並調用核心方法
NewsCell *cell = params[@"cell"];
NSString *imageName = params[@"imageName"];
[self doSomething:cell imageName:imageName];
}
- (void)doSomething:(NewsCell *)cell imageName:(NSString *)imageName {
// 核心邏輯
NSLog(@"Cell: %@, Image: %@", cell, imageName);
// 注意:UI 更新必須回主線程
dispatch_async(dispatch_get_main_queue(), ^{
// 更新 UI
});
}
performSelectorInBackground 屬於舊式 API,**強烈建議使用 GCD / OperationQueue**:
- (void)viewDidLoad {
[super viewDidLoad];
NewsCell *cell = ...;
NSString *imageName = @"image.png";
// 直接在 Block 中捕獲參數,無需封裝
dispatch_queue_t bgQueue = dispatch_get_global_queue(QOS_CLASS_USER_INITIATED, 0);
dispatch_async(bgQueue, ^{
// 後台執行耗時操作
NSString *result = [self processCell:cell imageName:imageName];
// 回主線程更新 UI
dispatch_async(dispatch_get_main_queue(), ^{
[self updateUIWithResult:result];
});
});
}
- (NSString *)processCell:(NewsCell *)cell imageName:(NSString *)imageName {
// 耗時邏輯
return @"processed";
}
- (void)updateUIWithResult:(NSString *)result {
// UI 更新
}
可封裝多參數調用,但代碼冗長,不推薦日常使用。
| 方案 | 優點 | 缺點 | 適用場景 |
|---|---|---|---|
| NSDictionary 封裝 | 兼容 performSelector API | 需手動封裝/解包、類型安全弱 | 維護舊代碼、必須用 performSelector |
| GCD Block | 語法簡潔、自動捕獲變量、類型安全、現代標準 | 需理解 Block 內存管理 | **所有新代碼首選** |
| OperationQueue | 支援依賴、優先級、取消、KVO | 稍繁瑣 | 複雜任務佇列管理 |
self 會 retain,注意循環引用:__weak typeof(self) weakSelf = self; 在 Block 內使用 weakSelf。performSelectorInBackground 不會自動 retain 參數,若參數在後台執行前被釋放會 Crash。**GCD Block 捕獲會自動 retain**,更安全。**棄用 performSelectorInBackground,改用 GCD dispatch_async**。Block 自然捕獲多參數,代碼清晰、類型安全、內存管理可控。若必須用舊 API,用 NSDictionary 封裝參數傳遞給包裝方法再解包。