使用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
相关产品推荐
相关产品推荐

