代碼按**同步思維**寫異步請求:
[manager GET:... success:^(...) {
arrData = ...; // 異步賦值
} failure:...];
NSLog(@"%@", arrData); // 同步執行,此時 arrData 仍為 nil
Block 內代碼在網絡響應返回時才執行,而外部代碼立即繼續執行,故拿不到數據。
- (void)viewDidLoad {
[super viewDidLoad];
// ... 初始化 tableView ...
[manager GET:url parameters:nil progress:nil success:^(AFHTTPRequestOperation *operation, id responseObject) {
// 1. 解析數據
NSError *err = nil;
XiaHuaModel *xiaHua = [[XiaHuaModel alloc] initWithDictionary:responseObject error:&err];
self.arrData = [NSMutableArray arrayWithArray:xiaHua.detail]; // 用 self. 觸發 KVO/屬性觀察
// 2. **主線程刷新 UI**(AFNetworking 回調已在主線程,但顯式寫法更安全)
dispatch_async(dispatch_get_main_queue(), ^{
[self.xiaoHuaView reloadData]; // 關鍵:通知 tableView 重新查詢 dataSource
});
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
// 錯誤處理:彈窗、重試按鈕、日誌上報
NSLog(@"Error: %@", error);
}];
}
| 錯誤做法 | 正確做法 | 理由 |
|---|---|---|
| 期望 Block 外直接用變量 | 在 Block 內賦值並觸發刷新 | 異步非阻塞,時間線不同步 |
用 __block 修飾局部變量 | 用 屬性 @property (nonatomic, strong) NSMutableArray *arrData; | 屬性持有強引用,生命周期受控;局部變量易釋放 |
| 忽略失敗回調 | 必寫 failure 塊,處理斷網、服務端錯誤 | 用戶體驗與穩健性 |
| 在後台線程刷新 UI | dispatch_async(dispatch_get_main_queue(), ^{ [tableView reloadData]; }) | UIKit 非線程安全,必須主線程 |
// OC 類別方法
+ (void)fetchJokesWithCompletion:(void (^)(NSArray * _Nullable, NSError * _Nullable))completion {
[manager GET:url parameters:nil success:^(id responseObject) {
XiaHuaModel *model = [[XiaHuaModel alloc] initWithDictionary:responseObject error:nil];
completion(model.detail, nil);
} failure:^(NSError *error) {
completion(nil, error);
}];
}
// 調用處
[ViewController fetchJokesWithCompletion:^(NSArray *jokes, NSError *error) {
if (jokes) {
self.arrData = [NSMutableArray arrayWithArray:jokes];
[self.tableView reloadData];
} else {
// show error
}
}];
異步編程核心:不要問「何時能拿到數據」,而要想「拿到數據後做什麼」,並把「做什麼」寫在回調/完成塊裡。