Swift中DispatchGroup异常求助:API循环调用未完成notify已提前执行
这种情况我之前踩过好几次坑,大概率是DispatchGroup的enter()和leave()没配对好,或者调用时机出了问题。下面给你几个具体的排查方向和解决办法:
先检查
enter()和leave()的配对数量
循环里每发起一个API请求前,必须调用一次enter(),而且调用次数要和循环的总次数完全一致。比如循环遍历10条数据发起请求,就得调用10次enter(),少一次都会导致notify()提前触发。如果循环里有条件判断跳过部分请求,一定要确保跳过的请求不会多调用enter(),也不会漏调用。确保
leave()在API的异步回调里执行
这是最容易犯的错误:把leave()写在API调用语句的外面,而不是异步完成的闭包里。比如错误写法:group.enter() myAsyncAPICall() // 这是异步请求,会立刻返回 group.leave() // 这里会马上执行,根本等不到API响应正确的做法是,不管API请求成功还是失败,都要在回调闭包里调用
leave():group.enter() myAsyncAPICall { result in // 处理请求结果 group.leave() // 必须放在异步回调内 }确认
notify()的调用时机
一定要等所有循环里的enter()和API请求都发起完成后,再调用notify()。如果把notify()写在循环内部,或者循环还没结束就执行了,notify()只会等待当前已经发起的请求完成,而不是全部循环的请求。正确的顺序应该是:let group = DispatchGroup() // 先遍历发起所有请求 for data in dataList { group.enter() fetchData(with: data) { // 处理数据 group.leave() } } // 所有请求发起后,再设置notify group.notify(queue: .main) { // 这里更新UI,确保所有请求都完成了 }排查线程队列的干扰
如果你的API调用本身是在主队列执行的,而notify()也指定了主队列,偶尔可能会因为队列阻塞导致顺序混乱。可以试试把API调用放到全局并发队列里发起,确保异步执行不阻塞:DispatchQueue.global().async { group.enter() myAsyncAPICall { result in // 处理结果 group.leave() } }用
wait()做临时测试(注意避坑)
你可以在非主队列里调用group.wait(),看看是否能正确等待所有请求完成。如果wait()能正常等待,说明notify()的队列或调用时机有问题;如果wait()也提前返回,那肯定是enter()和leave()的配对出了问题。注意:绝对不能在主队列调用wait(),会直接导致死锁!
最常见的问题就是enter/leave不配对,或者leave没放在异步回调里,先从这两点查起,大概率能解决问题。
内容的提问来源于stack exchange,提问作者Guilherme Giani

