Dispatch Group使用异常:API重构中通知时机提前引发的问题
解决Dispatch Group提前触发notify的问题
嘿,我太懂你这种明明想等所有API请求完再更新UI,结果Dispatch Group直接提前触发notify的崩溃感了!咱们先把问题根源理清楚,再一步步 fix 它。
核心问题所在
Dispatch Group的notify方法触发逻辑是:当组内所有调用过的enter()都被对应的leave()抵消后,才会执行闭包里的代码。但如果你的代码里,notify被调用时组里还没有任何enter记录,它会直接认为“所有任务都完成了”,立刻触发主队列操作——这就是你遇到流程混乱的原因。
一步步解决方案
1. 严格遵循「先enter,再发起异步请求,最后notify」的顺序
这是最关键的一点,绝对不能搞反顺序。每个API请求发起前先调用enter(),请求完成(不管成功失败)后调用leave(),所有请求都发起完再设置notify。示例代码:
// 1. 先创建Dispatch Group实例 let apiDispatchGroup = DispatchGroup() // 2. 第一个API请求:先enter,再发起请求 apiDispatchGroup.enter() YourAPIClient.fetchUserInfo { [weak self] result in guard let self = self else { return } switch result { case .success(let user): self.userData = user case .failure(let error): print("获取用户信息失败:\(error)") } // 不管成功失败,必须调用leave apiDispatchGroup.leave() } // 3. 第二个API请求:同样的逻辑 apiDispatchGroup.enter() YourAPIClient.fetchFeedItems { [weak self] result in guard let self = self else { return } switch result { case .success(let feeds): self.feedData = feeds case .failure(let error): print("获取Feed失败:\(error)") } apiDispatchGroup.leave() } // 4. 所有请求都发起后,再设置notify apiDispatchGroup.notify(queue: .main) { [weak self] in guard let self = self else { return } // 现在所有数据都准备好了,安全更新CollectionView self.collectionView.reloadData() }
2. 绝对不能遗漏任何leave()
哪怕API请求超时、失败,也要在错误回调里调用leave(),不然Dispatch Group会一直处于“等待未完成任务”的状态,notify永远不会触发,你的UI也永远不会更新。
3. 检查是否提前调用了notify
最容易踩的坑就是把notify写在了发起请求之前,比如错误示例:
let group = DispatchGroup() // ❌ 错误:先调用了notify,再处理请求 group.notify(queue: .main) { print("更新UI") } group.enter() YourAPIClient.fetchData { _ in group.leave() }
这种情况下,调用notify时组里还没有任何enter,所以会立刻执行闭包内容。
4. 循环处理多个API请求的正确姿势
如果是遍历一组URL或参数发起请求,记得在循环体内每次都调用enter(),回调里调用leave():
let group = DispatchGroup() let requestIds = [101, 102, 103] for id in requestIds { group.enter() YourAPIClient.fetchItem(with: id) { result in // 处理单条数据 group.leave() } } group.notify(queue: .main) { // 所有数据处理完毕,更新UI self.collectionView.reloadData() }
最后再划个重点
enter()必须在异步任务开始前调用,每个任务对应一次enter- 每个任务的回调(成功/失败/超时)里必须调用一次
leave(),保证和enter数量匹配 notify()必须在所有任务都发起后再调用
这样就能确保Dispatch Group乖乖等所有API请求完成后,再触发CollectionView的更新,再也不会出现提前重载的问题啦!
内容的提问来源于stack exchange,提问作者Bo Jackson
相关产品推荐
相关产品推荐

