核心数据并发调试

Posted

技术标签:

【中文标题】核心数据并发调试【英文标题】:Core Data Concurrency Debugging 【发布时间】:2017-04-05 21:11:51 【问题描述】:

这是我的 CoreDataStack 文件。当我通过并发调试运行项目时,我的应用程序在添加 pin 后立即崩溃。我认为这是因为我尝试在与创建上下文的线程不同的线程中访问数据。为了解决这个问题,我需要所有数据访问都发生在与创建上下文的线程相同的线程中。我做错了什么?

Run project with -com.apple.CoreData.ConcurrencyDebug 1

import CoreData

struct CoreDataStack 

    // MARK: - Properties

    private let model: NSManagedObjectModel
    internal let coordinator: NSPersistentStoreCoordinator
    private let modelURL: URL
    internal let dbURL: URL
    internal let persistingContext: NSManagedObjectContext
    internal let backgroundContext: NSManagedObjectContext
    let context: NSManagedObjectContext

    // MARK: - Initializers

    init?(modelName: String) 

        // Assumes the model is in the main bundle
        guard let modelURL = Bundle.main.url(forResource: modelName, withExtension: "momd") else 
            print("Unable to find \(modelName)in the main bundle")
            return nil
        
        self.modelURL = modelURL

        // Try to create the model from the URL
        guard let model = NSManagedObjectModel(contentsOf: modelURL) else 
            print("unable to create a model from \(modelURL)")
            return nil
        
        self.model = model

        // Create the store coordinator
        coordinator = NSPersistentStoreCoordinator(managedObjectModel: model)

        // Create a persistingContext (private queue) and a child one (main queue)
        // create a context and add connect it to the coordinator
        persistingContext = NSManagedObjectContext(concurrencyType: .privateQueueConcurrencyType)
        persistingContext.persistentStoreCoordinator = coordinator

        context = NSManagedObjectContext(concurrencyType: .mainQueueConcurrencyType)
        context.parent = persistingContext

        // Create a background context child of main context
        backgroundContext = NSManagedObjectContext(concurrencyType: .privateQueueConcurrencyType)
        backgroundContext.parent = context

        // Add a SQLite store located in the documents folder
        let fm = FileManager.default

        guard let docUrl = fm.urls(for: .documentDirectory, in: .userDomainMask).first else 
            print("Unable to reach the documents folder")
            return nil
        

        self.dbURL = docUrl.appendingPathComponent("model.sqlite")

        // Options for migration
        let options = [NSInferMappingModelAutomaticallyOption: true,NSMigratePersistentStoresAutomaticallyOption: true]

        do 
            try addStoreCoordinator(NSSQLiteStoreType, configuration: nil, storeURL: dbURL, options: options as [NSObject : AnyObject]?)
         catch 
            print("unable to add store at \(dbURL)")
        
    

    // MARK: - Utils

    func addStoreCoordinator(_ storeType: String, configuration: String?, storeURL: URL, options : [NSObject:AnyObject]?) throws 
        try coordinator.addPersistentStore(ofType: NSSQLiteStoreType, configurationName: nil, at: dbURL, options: nil)
    


// MARK: - CoreDataStack (Removing Data)

internal extension CoreDataStack  

    func dropAllData() throws 
        // delete all the objects in the db. This won't delete the files, it will
        // just leave empty tables.
        try coordinator.destroyPersistentStore(at: dbURL, ofType: NSSQLiteStoreType , options: nil)
        try addStoreCoordinator(NSSQLiteStoreType, configuration: nil, storeURL: dbURL, options: nil)
    


// MARK: - CoreDataStack (Batch Processing in the Background)

extension CoreDataStack 

    typealias Batch = (_ workerContext: NSManagedObjectContext) -> ()

    func performBackgroundBatchOperation(_ batch: @escaping Batch) 

        backgroundContext.perform() 

            batch(self.backgroundContext)

            // Save it to the parent context, so normal saving
            // can work
            do 
                try self.backgroundContext.save()
             catch 
                fatalError("Error while saving backgroundContext: \(error)")
            
        
    


// MARK: - CoreDataStack (Save Data)

extension CoreDataStack 

    func save() 
        context.performAndWait() 
            if self.context.hasChanges 
                do 
                    try self.context.save()
                 catch 
                    fatalError("Error while saving main context: \(error)")
                
                self.persistingContext.perform() 
                    do 
                        try self.persistingContext.save()
                     catch 
                        fatalError("Error while saving persisting context: \(error)")
                    
                
            
        
    

    func autoSave(_ delayInSeconds : Int) 
        if delayInSeconds > 0 
            do 
                try self.context.save()
                print("Autosaving")
             catch 
                print("Error while autosaving")
            
            let delayInNanoSeconds = UInt64(delayInSeconds) * NSEC_PER_SEC
            let time = DispatchTime.now() + Double(Int64(delayInNanoSeconds)) / Double(NSEC_PER_SEC)

            DispatchQueue.main.asyncAfter(deadline: time) 
                self.autoSave(delayInSeconds)
            
        
    

【问题讨论】:

【参考方案1】:

确保在主线程中进行所有 UI 更改。

【讨论】:

谢谢!我删除添加注释到主线程,它的工作原理!

以上是关于核心数据并发调试的主要内容,如果未能解决你的问题,请参考以下文章

[Linux 高并发服务器]GDB调试

如何调试从执行发布文件生成的核心转储文件?

ORACLE EBS 并发请求启用TRACE调试

如何调试阿帕奇?我在哪里可以找到核心转储

高并发性能调试经验分享

高并发性能调试经验分享