Core Data批量插入随机失败求助(Error 134030)
问题分析与解决:Core Data批量插入随机失败(NSCocoaErrorDomain Code=134030)
核心原因
- 频繁触发上下文保存:每次插入单个对象就调用
context.save(),会导致Core Data频繁提交事务,不仅效率低下,还容易引发上下文状态的一致性冲突——循环中前一次save的状态尚未完全同步,后续插入操作就已启动,进而触发134030这类一致性错误。 - 线程安全隐患:你通过
await获取viewContext,但viewContext是绑定主线程的上下文。如果插入操作在后台线程执行,即便使用performAndWait,频繁的跨线程save操作也会破坏上下文的线程安全,引发状态混乱。 - 唯一约束冲突(高频诱因):错误提示提到“同一ID多次报错”,说明
SinglePerson模型的id字段大概率设置了唯一约束(Unique),而待插入的groupOfPeople中存在重复ID,插入时触发约束冲突导致save失败。且失败后未清理上下文错误状态,后续所有操作都在已污染的上下文中执行,最终导致全部插入失败。 - 上下文状态未清理:单次save失败后,上下文会保留错误对象与异常状态,若未执行回滚或重置,后续save操作都会因上下文处于错误状态而失败。这就是为什么上下文查询显示150条数据,但SQLite实际只有部分数据的原因——失败前的对象已提交到数据库,失败后的仅存在于上下文内存中,并未写入持久化存储。
解决办法
1. 批量插入,统一提交
放弃每次插入都save的逻辑,完成所有对象插入后一次性调用save,减少事务提交次数,避免频繁同步带来的冲突:
// 修改调用逻辑 let context = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext var counter = 0 do { for person in groupOfPeople { let newSinglePerson = SinglePerson(context: context) newSinglePerson.id = person.ID newSinglePerson.name = person.name newSinglePerson.age = person.age // ...其他属性赋值 counter += 1 } // 统一保存所有插入的对象 try context.save() logComment = logComment + "..success!(\(counter))" } catch let error { print(error) Logging.insertError(message: "IMPORT ERROR: \(error.localizedDescription)", location: "buildDatabase20") // 失败后回滚上下文,清除错误状态 context.rollback() }
2. 改用后台上下文处理批量操作
如果插入操作在后台执行,不要使用主线程的viewContext,创建私有后台上下文保证线程安全:
let container = (UIApplication.shared.delegate as! AppDelegate).persistentContainer let backgroundContext = container.newBackgroundContext() backgroundContext.perform { [weak self] in guard let self = self else { return } var counter = 0 do { for person in groupOfPeople { let newSinglePerson = SinglePerson(context: backgroundContext) newSinglePerson.id = person.ID newSinglePerson.name = person.name newSinglePerson.age = person.age // ...其他属性赋值 counter += 1 } try backgroundContext.save() // 回到主线程更新日志 DispatchQueue.main.async { self.logComment = self.logComment + "..success!(\(counter))" } } catch let error { print(error) Logging.insertError(message: "IMPORT ERROR: \(error.localizedDescription)", location: "buildDatabase20") backgroundContext.rollback() } }
3. 处理唯一约束冲突
- 提前过滤重复数据:在插入前对
groupOfPeople去重,确保ID唯一:// 利用字典去重,保留最后一个出现的对象(可根据需求调整规则) let uniquePeople = Dictionary(grouping: groupOfPeople, by: { $0.ID }).map { $0.value.last! } - 插入前检查存在性:如果无法提前过滤,在插入单个对象前先查询上下文是否已有相同ID的对象:
func saveSinglePerson(sPerson: singlePerson, context: NSManagedObjectContext) throws { let fetchRequest: NSFetchRequest<SinglePerson> = SinglePerson.fetchRequest() fetchRequest.predicate = NSPredicate(format: "id == %@", sPerson.ID as CVarArg) let count = try context.count(for: fetchRequest) if count == 0 { let newSinglePerson = SinglePerson(context: context) newSinglePerson.id = sPerson.ID newSinglePerson.name = sPerson.name newSinglePerson.age = sPerson.age // ...其他属性赋值 } }
4. 错误发生后强制清理上下文状态
无论哪种场景,save失败时必须调用context.rollback()重置上下文状态,避免后续操作受错误状态影响:
do { try context.save() } catch { print(error) Logging.insertError(message: "IMPORT ERROR: \(error.localizedDescription)", location: "buildDatabase20") context.rollback() // 重置上下文,清除错误对象与异常状态 }
5. 移除不必要的await
viewContext是同步获取的主线程上下文,不需要用await,多余的异步获取可能导致线程切换,引发不必要的状态问题。
内容的提问来源于stack exchange,提问作者blendstylez
相关产品推荐
相关产品推荐

