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

DispatchGroup加载Rick and Morty API角色列表时滚动追加加载失效及EXC_BREAKPOINT错误解决咨询

DispatchGroup加载Rick and Morty API角色列表时滚动追加加载失效及EXC_BREAKPOINT错误解决咨询

嘿,我看你在用Rick and Morty API做剧集列表的时候,碰到了个头疼的问题——初始加载用DispatchGroup能正常拉取每个剧集的角色列表,但滚动触发追加加载就失效了,还出现了EXC_BREAKPOINT错误。我来帮你拆解下可能的原因和解决办法:

1. DispatchGroup的复用或生命周期问题

很多时候追加加载失效,是因为DispatchGroup没有正确初始化或者被重复使用了。比如你初始加载时创建了一个Group,但追加加载时没新建,而是复用了旧的,这会导致后续的enter/leave信号混乱,要么触发死锁,要么notify永远不执行。

解决思路:每次追加加载时都要新建一个DispatchGroup,确保每一批角色请求都有独立的分组管理。比如:

// 追加加载时的角色请求逻辑
let group = DispatchGroup()
for url in newEpisode.characterUrls {
    group.enter()
    URLSession.shared.dataTask(with: URL(string: url)!) { data, _, error in
        defer { group.leave() } // 确保无论请求成功失败都能离开分组
        // 处理角色数据解析...
    }.resume()
}
group.notify(queue: .main) {
    // 更新数据源+通知UI刷新
}

2. loadingMore标记的线程安全问题

你的loadingMore变量是用来防止重复触发加载的,但如果在多线程环境下直接修改(比如网络回调在后台线程,滚动触发在主线程),很容易出现竞态条件——比如多次触发加载,或者标记没正确更新导致后续加载被阻塞。

解决思路:给loadingMore加线程保护,用串行队列来处理读写操作:

private let loadingLockQueue = DispatchQueue(label: "com.rm.episode.loading.lock")
private var _loadingMore = false
private var loadingMore: Bool {
    get { loadingLockQueue.sync { _loadingMore } }
    set { loadingLockQueue.sync { _loadingMore = newValue } }
}

3. EXC_BREAKPOINT错误的核心原因

这个错误大多是强制解包nil值或者数组越界导致的:

  • 检查你处理角色URL时,有没有无效URL导致解析失败,后续模型数据为nil,然后在UI渲染时强制解包了;
  • 检查通知delegate的newIndexpath是否基于更新后的episodeViewModels数组生成,避免索引越界。

解决思路:所有可选类型都用if let/guard let做安全处理,比如角色解析时:

guard let character = try? JSONDecoder().decode(RMCharacter.self, from: data) else {
    return
}

生成新索引路径时,确保是基于更新后的数组长度:

let startIndex = self.episodeViewModels.count - newViewModels.count
let newIndexPaths = (startIndex..<self.episodeViewModels.count).map { 
    IndexPath(row: $0, section: 0) 
}

4. 分页参数与Delegate回调时机问题

  • 确认你正确使用了Rick and Morty API的分页参数:初始加载后要从info?.next里获取下一页的请求地址,避免分页错误导致加载不到新数据;
  • 确保在DispatchGroup的notify回调里先更新数据源,再通知UI刷新,而且必须在主线程执行,避免UI操作在后台线程触发异常。

修正后的追加加载逻辑示例

func fetchAdditionalEpisodes() {
    guard !loadingMore, let nextPageUrl = info?.next else { return }
    loadingMore = true
    
    URLSession.shared.dataTask(with: URL(string: nextPageUrl)!) { [weak self] data, _, error in
        guard let self = self, let data = data, error == nil else {
            self?.loadingMore = false
            return
        }
        
        do {
            let episodeResponse = try JSONDecoder().decode(RMEpisodeResponse.self, from: data)
            self.info = episodeResponse.info
            let newEpisodeViewModels = episodeResponse.results.map(RMEpisodeViewCellViewModel.init)
            
            let group = DispatchGroup()
            for vm in newEpisodeViewModels {
                for charUrl in vm.characterUrls {
                    group.enter()
                    URLSession.shared.dataTask(with: URL(string: charUrl)!) { charData, _, charError in
                        defer { group.leave() }
                        guard let charData = charData, charError == nil else { return }
                        if let character = try? JSONDecoder().decode(RMCharacter.self, from: charData) {
                            vm.addCharacter(character)
                        }
                    }.resume()
                }
            }
            
            group.notify(queue: .main) {
                self.loadingMore = false
                self.episodeViewModels.append(contentsOf: newEpisodeViewModels)
                let startIdx = self.episodeViewModels.count - newEpisodeViewModels.count
                let indexPaths = (startIdx..<self.episodeViewModels.count).map { IndexPath(row: $0, section: 0) }
                self.delegate?.didLoadAdditionalCharacters(with: indexPaths)
            }
            
        } catch {
            self.loadingMore = false
            print("剧集解析错误:\(error.localizedDescription)")
        }
    }.resume()
}

备注:内容来源于stack exchange,提问作者Almat Kairatov

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.16 09:44:36