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

使用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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.12 04:59:20