【问题标题】:Core data crash with method "hasChanges"核心数据崩溃与方法“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

我已经得出结论:

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

【问题讨论】:

标签: ios swift core-data


【解决方案1】:

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2014-11-09
    • 2023-03-14
    • 2018-09-02
    • 2023-03-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多