处理父子 managedObjectContexts 的应用程序崩溃

Posted

技术标签:

【中文标题】处理父子 managedObjectContexts 的应用程序崩溃【英文标题】:Handling app crashes for Parent-Child managedObjectContexts 【发布时间】:2015-09-15 05:14:34 【问题描述】:

这是我创建的示例应用程序应用程序,用于模拟我正在处理的案例。它是一个使用 CoreData 和 Swift 2.0 和 Xcode 7 beta4 的单视图应用程序

因此,在我的视图控制器中,我创建了一个 privateObjectContext,它是 mainManagedObjectContext 的子级

let mainMOC = AppDelegate().managedObjectContext
var privateObjectContext : NSManagedObjectContext?

override func viewDidLoad() 
    super.viewDidLoad()
    // Do any additional setup after loading the view, typically from a nib.

    privateObjectContext = NSManagedObjectContext(concurrencyType: .PrivateQueueConcurrencyType)
    privateObjectContext?.parentContext = mainMOC

我知道在子 MOC 中保存会将其同步到父级,然后保存父级会将其保存到持久存储中,但是每次我保存子 MOC 时都保存主 MOC 没有意义,并且使子 MOC 的目的MOC 冗余。所以在我完成所有测试后,我保存了我的父 MOC,它确实按预期存储在持久存储中。

但是当我模拟应用程序崩溃时(通过转到任务管理器并强制终止应用程序)它不会存储在持久管理器中;因此它应该有这个

func applicationWillTerminate(application: UIApplication) 
        // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
        // Saves changes in the application's managed object context before the application terminates.
        self.saveContext()
    

func saveContext () 
        if managedObjectContext.hasChanges 
            do 
                try managedObjectContext.save()
             catch 
                // Replace this implementation with code to handle the error appropriately.
                // abort() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
                let nserror = error as NSError
                NSLog("Unresolved error \(nserror), \(nserror.userInfo)")
                abort()
            
        
    

但是 managedObjectContext.hasChanges 返回 false,尽管它在 ViewController 中发生了变化 我在 MOC 生命周期的某个地方错了吗?

--编辑 我使用 NSManagedObjectContext 的扩展来批量创建测试对象

privateObjectContext?.performBlock 
            self.privateObjectContext?.createTestObjects(100) 
                (person: Person, index) in
                person.name = "Test Person \(index)"
            
            do 
                try self.privateObjectContext?.save()
             catch 
                print(error)
            

        

在此之后,如果我从我的主 MOC 获取,那么我会得到预期的 100 条记录。

print(mainMOC.hasChanges) //true
let persons = mainMOC.fetchAll(Person)
print(persons.count) //100

但是在应用程序强行崩溃后,主 MOC 仍然没有任何变化。

【问题讨论】:

在您的案例中使用子上下文的动机是什么?您是否看到任何并发使用情况? 我有很多数据需要在我的主应用程序中定期保存在后台。因此,正在考虑通过子 MOC 将其异步保存到 MOC 的选项,然后当用户完成将其存储在持久存储中的任务时。当应用程序在任务之间崩溃时,我必须处理这种情况,因此问题 理想情况下,如果您打算立即将更新写回磁盘,则在父上下文中的保存应紧跟在子上下文中的保存之后。 我实际上并不想立即将更改写入磁盘并占用我的主队列,但前提是应用程序崩溃或其他情况 【参考方案1】:

应用程序将终止的方法基本上是从不使用的,所以你不应该依赖它。您也不应该真正编写代码以节省崩溃,首先您的应用程序不应该崩溃,其次很难知道崩溃时哪些数据无效,因此您可能会破坏其他良好的数据。

一般来说,您应该立即保存或批量保存到永久存储。

请注意,您还可以以不同的方式构建托管对象上下文,以便处理和保存事物,然后将其合并到主上下文中。如果您在保存时确实在 UI 上看到问题,您可能只想进行这项工作。


严格来说,问题在于您没有保存子 MOC(至少在我们可以看到的代码中没有)。因此,当您保存主 MOC 时,它还没有任何子级更改,也没有什么要保存的。

【讨论】:

我考虑的情况是应用程序由于某种原因崩溃的最坏情况。我肯定会不时将数据保存到持久协调器。但是当我杀死它时会调用applicationWillTerminate 方法,但 MOC 没有显示任何变化。这是为什么呢? 嗨..我已经编辑了我的代码以显示我保存我的子 MOC 的测试用例,它确实反映在主 MOC 中,但它没有显示 ApllicationWillTerminate 方法中的更改。 NSManagedObjectContext 是一个类,所以应该正确引用,或者我可能在它的生命周期中遗漏了一些东西【参考方案2】:

如果您要进行大量保存,并且正如@Wain 正确指出的那样,在终止时执行保存不是一个好主意,那么有一种更推荐的方法可以在不阻塞主线程的情况下执行此操作。您将需要创建一个根上下文,它与存储协调器关联并在非主队列中运行。您的主要上下文可以是此上下文的子上下文。

请参阅

中的“异步保存”部分

https://www.cocoanetics.com/2012/07/multi-context-coredata/

【讨论】:

我可以调查一下。谢谢!!虽然我仍然不明白为什么即使在保存了我的孩子 MOC 之后主 MOC 也没有变化 好的,有没有可能看看你的实际代码?

以上是关于处理父子 managedObjectContexts 的应用程序崩溃的主要内容,如果未能解决你的问题,请参考以下文章

NSManagedObject 总是在同一个 ManagedObjectContext 上创建

尝试从 iOS 中的 Singleton 类初始化 ManagedObjectContext

self.managedObjectContext 和 managedObjectInstance.managedObjectContext 有啥区别?

父子进程捕捉信号的特点

java异常处理(父子异常的处理)

单例 managedObjectContext