使用MagicalRecord保存NSManagedObject失败,如何正确保存ExerciseEntity?
如何正确保存MagicalRecord创建的ExerciseEntity实例?
问题背景
我创建了一个对象:
let exercise = ExerciseEntity.mr_createEntity()
并编写了如下方法:
func updateLocalExercise(with exercise: ExerciseEntity, completion: @escaping (ResultInfo<[ExerciseEntity]>) -> Void) { MagicalRecord.save({ (context) in if let localExercise = exercise.mr_(context) { // 此处实际为nil,日志中抛出错误 } }) }
现在遇到的问题是:通过ExerciseEntity.mr_createEntity()创建的实例,在保存闭包里获取对应上下文的实例时返回nil,请问该如何正确保存这个实例?
问题原因
你遇到的核心问题是NSManagedObject上下文隔离:
ExerciseEntity.mr_createEntity()默认会在**主上下文(NSManagedObjectContext.mr_default())**中创建实体- 而
MagicalRecord.save(_:)闭包里传入的context是一个后台私有上下文,不同上下文的NSManagedObject实例不能直接跨上下文访问,所以你调用的exercise.mr_(context)(应该是mr_inContext(context)的写法错误)会返回nil
正确解决方案
方案1:直接在目标上下文创建实体
如果你的业务允许,最好直接在保存闭包的后台上下文中创建实体,这样就不需要跨上下文迁移:
func updateLocalExercise(completion: @escaping (ResultInfo<[ExerciseEntity]>) -> Void) { MagicalRecord.save({ (context) in // 在当前后台上下文创建实体 guard let exercise = ExerciseEntity.mr_createEntity(in: context) else { // 处理创建失败的情况 return } // 这里设置exercise的属性,比如: // exercise.name = "上肢训练" // exercise.duration = 30 }) { (success, error) in // 保存完成后,从主上下文获取最新数据返回 if success { let latestExercises = ExerciseEntity.mr_findAll() as? [ExerciseEntity] ?? [] completion(.success(latestExercises)) } else { let defaultError = NSError(domain: "MagicalRecord", code: -1, userInfo: [NSLocalizedDescriptionKey: "保存训练数据失败"]) completion(.failure(error ?? defaultError)) } } }
方案2:将主上下文的实体迁移到后台上下文
如果实体已经在主上下文创建好了,需要用mr_inContext(_:)方法将其迁移到保存闭包的后台上下文,再进行修改和保存:
func updateLocalExercise(with exercise: ExerciseEntity, completion: @escaping (ResultInfo<[ExerciseEntity]>) -> Void) { MagicalRecord.save({ (context) in // 将主上下文的实体迁移到当前后台上下文 guard let localExercise = exercise.mr_inContext(context) else { // 处理迁移失败的情况 return } // 在这里修改localExercise的属性 // localExercise.lastUpdated = Date() }) { (success, error) in // 保存完成后,主上下文会自动合并后台上下文的变更,直接从主上下文取数据即可 if success { let updatedExercises = ExerciseEntity.mr_findAll() as? [ExerciseEntity] ?? [] completion(.success(updatedExercises)) } else { let defaultError = NSError(domain: "MagicalRecord", code: -1, userInfo: [NSLocalizedDescriptionKey: "更新训练数据失败"]) completion(.failure(error ?? defaultError)) } } }
额外注意点
- 确保你调用的是正确的方法:
mr_inContext(_:),而不是你代码里的mr_(context)(这应该是拼写错误) - 保存完成后的回调里,一定要从主上下文获取数据,因为后台上下文的变更会自动合并到主上下文,主上下文的数据才是最新的
- 如果需要批量操作或者复杂的上下文管理,可以考虑使用
MagicalRecord的mr_saveToPersistentStoreAndWait()同步方法,但注意不要在主线程调用同步方法,避免阻塞UI
内容的提问来源于stack exchange,提问作者Oleksandr Matrosov
相关产品推荐
相关产品推荐

