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

使用Core Data后台上下文获取远程数据时代码立即崩溃如何解决

代码问题排查与修复方案
  • 缺少日期解码策略配置:请求的friendface.json接口返回数据包含ISO8601格式的日期字段,JSONDecoder默认的日期解码策略无法识别该格式,会直接触发解码崩溃。
  • 后台上下文未执行保存操作:通过performBackgroundTask获取的私有上下文解码得到的Core Data对象,没有调用save()方法保存变更,不仅数据无法持久化存储,上下文销毁后对象会直接失效,后续操作也可能触发野指针崩溃。
  • 循环引用风险:URLSession的dataTask闭包没有使用弱引用持有self,会导致循环引用,DataProvider对象无法正常释放。
  • 线程与上下文跨界访问问题:在私有队列的context闭包内直接给self.users属性赋值,且赋值的是绑定私有上下文的User对象。如果users属性后续在主线程访问,会触发跨上下文、跨线程访问Core Data对象的崩溃。
  • 错误全被静默吞掉:所有可能抛出错误的操作都用try?处理,解码失败、请求失败、fetch失败的原因完全无法定位,不利于问题排查。
  • 冗余的context.perform调用:performBackgroundTask本身已经保证闭包运行在对应上下文的专属队列中,内部不需要再额外调用context.perform,属于冗余代码。
修复后代码
class DataProvider {
    let persistentContainer: NSPersistentContainer
    // 建议仅在主线程访问该属性,存储绑定主上下文的对象
    var users: [User]? = []
    
    func loadData() {
        let url = URL(string: "https://www.hackingwithswift.com/samples/friendface.json")!
        var request = URLRequest(url: url)
        request.httpMethod = "GET"
        URLSession.shared.dataTask(with: request) { [weak self] data, response, error in
            // 处理请求错误
            if let error = error {
                print("请求失败:\(error.localizedDescription)")
                return
            }
            guard let data = data else {
                print("响应数据为空")
                return
            }
            
            guard let self = self else { return }
            self.persistentContainer.performBackgroundTask { privateContext in
                let decoder = JSONDecoder()
                decoder.userInfo[CodingUserInfoKey.managedObjectContext] = privateContext
                // 配置ISO8601日期解码策略
                decoder.dateDecodingStrategy = .iso8601
                
                do {
                    // 解码数据
                    _ = try decoder.decode([User].self, from: data)
                    // 保存后台上下文变更
                    if privateContext.hasChanges {
                        try privateContext.save()
                    }
                    // 切换到主队列操作主上下文、给属性赋值
                    DispatchQueue.main.async {
                        let fetchRequest : NSFetchRequest<User> = User.fetchRequest()
                        do {
                            // 从主上下文fetch数据,保证线程安全
                            self.users = try self.persistentContainer.viewContext.fetch(fetchRequest)
                            // 可在此处添加UI刷新回调逻辑
                        } catch {
                            print("fetch用户数据失败:\(error.localizedDescription)")
                        }
                    }
                } catch {
                    print("解码失败:\(error.localizedDescription)")
                }
            }
        }.resume()
    }
    
    init(container: NSPersistentContainer) {
        self.persistentContainer = container
    }
}

内容的提问来源于stack exchange,提问作者pakobongbong

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.01 17:24:04