下拉刷新时Dispatch Group Notify刷新TableView异常求助
问题排查与解决方案
针对你遇到的「notify闭包中reloadData()执行了,但未打印Going to Refresh」的异常,结合你的代码逻辑,我们可以从以下几个方向逐一排查修复:
1. 确认DispatchGroup的生命周期问题
如果fetchGroup是下拉刷新函数中的局部变量,当函数执行完毕后,ARC会自动释放这个变量,可能导致notify闭包的执行出现异常(比如部分代码被跳过、执行逻辑不完整)。
修复方案:
将fetchGroup声明为视图控制器的实例属性,确保它在所有请求完成前不会被释放:
class YourViewController: UIViewController { private let fetchGroup = DispatchGroup() // ... 其他属性与方法 }
2. 检查enter()与leave()的调用配对
你的代码中,enter()是在if let URL = ...分支内调用的,如果某一个URL初始化失败(比如"fetch1"路径有误,导致URL(string:relativeTo:)返回nil),对应的enter()就不会执行。这种情况下,notify会在已执行的enter()对应的leave()完成后触发,虽然闭包会执行,但可能不符合你「三个请求都完成」的预期,甚至引发异常。
修复方案:
- 先验证三个URL是否都能成功创建,添加日志排查:
if let URL = URL(string: "fetch1", relativeTo: Property.baseURL) { // 请求代码 } else { print("Failed to create URL for fetch1") } - 确保
enter()和leave()严格配对,即使URL创建失败,也完成对应流程:let shouldEnter = URL(string: "fetch1", relativeTo: Property.baseURL) != nil if shouldEnter { fetchGroup.enter() } if let URL = URL(string: "fetch1", relativeTo: Property.baseURL) { // 请求代码 } else { // URL创建失败,手动完成leave if shouldEnter { fetchGroup.leave() } }
3. 确保leave()在所有分支都能执行
Alamofire的responseJSON闭包会在请求完成(成功/失败)时执行,但如果请求被取消或JSON解析抛出异常,可能导致leave()被遗漏。可以用defer语句保证leave()一定会执行:
修复方案:
Property.alamofireManager.request(URL, headers: headers).responseJSON{ [weak self] response in defer { print("LEAVE 1") self?.fetchGroup.leave() } // Manage json(即使这里抛出异常,defer内的代码也会执行) }
4. 验证主线程执行与控制台输出
虽然你指定了notify(queue: .main),但可以显式确认闭包的执行线程,同时检查Xcode控制台的过滤设置:
- 在
notify闭包中添加线程日志:fetchGroup.notify(queue: .main, execute: { print("Current thread: \(Thread.current)") // 应输出主线程信息 print("Going to Refresh") self.refreshControl.endRefreshing() self.tableView.reloadData() }) - 检查Xcode控制台:点击右上角
Filter按钮,确保选择All Output且无过滤条件。
优化后的完整代码示例
class YourViewController: UIViewController { private let fetchGroup = DispatchGroup() // ... 其他属性 @objc func handleRefresh() { self.refreshControl.beginRefreshing() // 处理请求1 let fetch1URL = URL(string: "fetch1", relativeTo: Property.baseURL) let shouldEnterFetch1 = fetch1URL != nil if shouldEnterFetch1 { fetchGroup.enter() } if let URL = fetch1URL { let headers: HTTPHeaders = [ "Authorization": Property.shared.token, "content-type": "application/json" ] Property.alamofireManager.request(URL, headers: headers).responseJSON{ [weak self] response in defer { print("LEAVE 1") self?.fetchGroup.leave() } // 处理JSON数据 } } else { print("Failed to create fetch1 URL") if shouldEnterFetch1 { fetchGroup.leave() } } // 处理请求2(逻辑与请求1一致) let fetch2URL = URL(string: "fetch2", relativeTo: Property.baseURL) let shouldEnterFetch2 = fetch2URL != nil if shouldEnterFetch2 { fetchGroup.enter() } if let URL = fetch2URL { let headers: HTTPHeaders = [ "Authorization": Property.shared.token, "content-type": "application/json" ] Property.alamofireManager.request(URL, headers: headers).responseJSON{ [weak self] response in defer { print("LEAVE 2") self?.fetchGroup.leave() } // 处理JSON数据 } } else { print("Failed to create fetch2 URL") if shouldEnterFetch2 { fetchGroup.leave() } } // 处理请求3(逻辑与请求1一致) let fetch3URL = URL(string: "fetch3", relativeTo: Property.baseURL) let shouldEnterFetch3 = fetch3URL != nil if shouldEnterFetch3 { fetchGroup.enter() } if let URL = fetch3URL { let headers: HTTPHeaders = [ "Authorization": Property.shared.token, "content-type": "application/json" ] Property.alamofireManager.request(URL, headers: headers).responseJSON{ [weak self] response in defer { print("LEAVE 3") self?.fetchGroup.leave() } // 处理JSON数据 } } else { print("Failed to create fetch3 URL") if shouldEnterFetch3 { fetchGroup.leave() } } fetchGroup.notify(queue: .main, execute: { [weak self] in guard let self = self else { return } print("Current thread: \(Thread.current)") print("Going to Refresh") self.refreshControl.endRefreshing() self.tableView.reloadData() }) } }
另外,建议在闭包中使用[weak self]避免循环引用,防止视图控制器无法被正常释放。
内容的提问来源于stack exchange,提问作者Bubu
相关产品推荐
相关产品推荐

