You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

下拉刷新时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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.21 04:03:03