核心数据崩溃与方法“hasChanges”

Posted

技术标签:

【中文标题】核心数据崩溃与方法“hasChanges”【英文标题】:Core data crash with method "hasChanges" 【发布时间】:2020-05-23 12:45:58 【问题描述】:

我有 CoreDataStack

我为 CoreData 调试添加了调试选项 "-com.apple.CoreData.ConcurrencyDebug 1"

class CoreDataStack 

public enum SaveStatus 
    case saved, rolledBack, hasNoChanges, error


private var modelName: String

var viewContext: NSManagedObjectContext

var privateContext: NSManagedObjectContext

var persisterContainer: NSPersistentContainer

init(_ modelName: String) 

    self.modelName = modelName

    let container = NSPersistentContainer(name: modelName)

    container.loadPersistentStores  persisterStoreDescription, error in

        print("CoreData", "Initiated \(persisterStoreDescription)")
        guard error == nil else 
            print("CoreData", "Unresolved error \(error!)")
            return
        
    

    self.persisterContainer = container

    self.viewContext = container.viewContext
    self.viewContext.automaticallyMergesChangesFromParent = true

    self.privateContext = NSManagedObjectContext(concurrencyType: .privateQueueConcurrencyType)
    self.privateContext.persistentStoreCoordinator = container.persistentStoreCoordinator
    self.privateContext.automaticallyMergesChangesFromParent = true



func createTemporaryViewContext() -> NSManagedObjectContext 
    let context = NSManagedObjectContext(concurrencyType: .mainQueueConcurrencyType)
    context.parent = self.privateContext
    context.automaticallyMergesChangesFromParent = true
    return context


func saveTempViewContext(tempContext context: NSManagedObjectContext, completion: ((CoreDataStack.SaveStatus) -> Void)? = nil) 

    guard context.hasChanges || privateContext.hasChanges else 
        completion?(.hasNoChanges)
        return
    

    context.performAndWait 
        do 
            try context.save()
        
        catch 
            completion?(.error)
            return
        
    

    privateContext.perform  [weak self] in
        do 
            try self?.privateContext.save()
            completion?(.saved)
        
        catch 
            self?.privateContext.rollback()
            completion?(.rolledBack)
        

    



class ViewController: UIViewController 

@objc
func save(_ sender: UIBarButtonItem) 

    let coreDataStack = CoreDataStack()

    let tempMainContext = coreDataStack.createTemporaryViewContext() //child main context from private context

    var people = People(context: tempMainContext)

    self.people.name = "John Doe"
    self.people.age = 25

    coreDataStack.saveTempViewContext(tempContext: tempMainContext)  status in

        print(status)

    







我已将“privateContext”附加到协调器

我从私有上下文创建了“tempMainContext”

当我调用“saveTempViewContext”时,我想保存将更改推送到父级 (privateContext) 的 tempMainContext,并且此 privateContext 保存到持久存储中

所以错误发生在一行

privateContext.hasChanges

我知道这行在主线程中执行。而且我需要调用方法“perform”或“performAndWait”在正确的队列上执行。

喜欢这个

    var contextHasChanges: Bool = false
    var privateContextHasChanges: Bool = false

    context.performAndWait 
        contextHasChanges = context.hasChanges
    

    privateContext.performAndWait 
        privateContextHasChanges = privateContext.hasChanges
    

    guard context.hasChanges || privateContext.hasChanges else 
        completion?(.hasNoChanges)
        return
    

但是调用“performAndWait”只是为了检查上下文是否发生了变化,这太奇怪了。当我调用“performAndWait”时,它会阻塞当前线程,在我的情况下它是 MainThread。而且我不想阻塞主线程,即使是很短的时间。

我们如何解决这个问题?

更新 1 附加调试堆栈

UPD 2

在下面的 CoreDataStack 初始化方法中,我添加了代码。 我只是检查私有上下文是否有变化,它会崩溃

let privateContextHasChanges = privateContext.hasChanges

我认为这是因为在那一行我们在 MainThread 并且我们接触了以“privateQueueConcurrencyType”初始化的私有上下文,我调查如果我接触其他属性,例如“privateContext.name”或“privateContext.parent”它工作正常.

但是如果我像这样触摸财产:

privateContext.hasChanges
privateContext.registeredObjects
privateContext.updatedObjects

也许其他

它会再次崩溃

所以我可以得出结论,这些属性不是线程安全的。

谁能确认一下?

更新 3 在我阅读帖子Unexpected Core Data Multithreading Violation

我已经得出结论:

    如果我在主线程上并且我的上下文类型是 .main 我不需要任何更改并且我很安全 如果我在某个地方并且我不知道自己想要哪种线程,我总是需要执行“perform”或“performAndWait”来同步附加到上下文的队列。 几乎总是“执行”而不是“执行和等待”,除非你不能没有它。

【问题讨论】:

你能附上崩溃堆栈跟踪吗? 我添加了一个带有崩溃堆栈的 img “hasChanges”是线程安全的吗? 请看这个:***.com/questions/41999983/… 【参考方案1】:

context.hasChanges 不是线程安全的。它需要在上下文线程中使用。 https://developer.apple.com/forums/thread/133378

【讨论】:

以上是关于核心数据崩溃与方法“hasChanges”的主要内容,如果未能解决你的问题,请参考以下文章

Kendo Grid hasChanges,如何处理只读网格?

SwiftUI hasChanges 总是返回 true

iPhone - 核心数据崩溃 - 多个托管对象上下文

SwiftUI Preview Canvas 与核心数据一起崩溃

与核心数据一起使用时预览崩溃

与故障相关的很少核心数据崩溃