Warning: file_get_contents(/data/phpspider/zhask/data//catemap/7/wcf/4.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Ios 处理AppDelegate上persistentContainer实例的正确方法_Ios_Swift_Core Data - Fatal编程技术网

Ios 处理AppDelegate上persistentContainer实例的正确方法

Ios 处理AppDelegate上persistentContainer实例的正确方法,ios,swift,core-data,Ios,Swift,Core Data,当我们在选中Core Data选项的Xcode中创建新项目时,它会在AppDelegate.swift上生成一个定义核心数据堆栈的新项目: class AppDelegate: UIResponder, UIApplicationDelegate { // ... // MARK: - Core Data stack lazy var persistentContainer: NSPersistentContainer = { let contain

当我们在选中Core Data选项的Xcode中创建新项目时,它会在AppDelegate.swift上生成一个定义核心数据堆栈的新项目:

class AppDelegate: UIResponder, UIApplicationDelegate {

    // ...

    // MARK: - Core Data stack

    lazy var persistentContainer: NSPersistentContainer = {
        let container = NSPersistentContainer(name: "CoreDataTest")
        container.loadPersistentStores(completionHandler: { (storeDescription, error) in
            if let error = error as NSError? {
                fatalError("Unresolved error \(error), \(error.userInfo)")
            }
        })
        return container
    }()

    // MARK: - Core Data Saving support

    func saveContext () {
        let context = persistentContainer.viewContext
        if context.hasChanges {
            do {
                try context.save()
            } catch {
                let nserror = error as NSError
                fatalError("Unresolved error \(nserror), \(nserror.userInfo)")
            }
        }
    }

}
为了方便访问
persistentContainer
,我还添加了以下代码:

static var persistentContainer: NSPersistentContainer {
    guard let appDelegate = UIApplication.shared.delegate as? AppDelegate else { fatalError("Could not convert delegate to AppDelegate") }
    return appDelegate.persistentContainer
}
所以我可以这样称呼它:

let container = AppDelegate.persistentContainer
当我试图从后台线程访问它时,问题就出现了。例如,我有一段代码在后台运行,并从web服务获取一些数据。当它获取数据时,我使用以下方式保存它:

static func loadData(_ id: String) {
    fetchDataOnBackground(id: id) { (error, response) in
        if let error = error {
            // handle...
            return
        }

        guard let data = response?.data else { return }

        let container = AppDelegate.persistentContainer // Here
        container.performBackgroundTask({ context in
            // save data...
        })
    }
}
当我尝试获取持久容器时,它会在控制台上生成:

Main Thread Checker: UI API called on a background thread: -[UIApplication delegate]
为了不再发生这种情况,我在
AppDelegate
上将我的
persistentcainer
lazy var
更改为
static

static var persistentContainer: NSPersistentContainer = {
    // same code as before...
}()
错误不再发生了

但我想知道这是否会有我不知道的副作用。我的意思是,反正我只有一个
persistentcainer
,因为只有一个
AppDelegate
实例,对吗?所以我可以像以前一样将其更改为静态,并在我的应用程序的其他部分上使用
AppDelegate.persistentcainer
访问它,而不会出现任何问题


或者是否有其他推荐的模式来处理
persistentcainer
实例化和使用?

persistentcainer
在主队列上运行。正如属性名称所示,此托管对象上下文旨在与应用程序的用户界面结合使用。您可能需要调度回主队列以与
uiapplicationelegate
persistentcainer
交互,使用:

DispatchQueue.main.async {
   // save data …
}
介绍 你好。我自己正在为一个应用程序处理核心数据,我发现有几种方法可以处理核心数据,通过线程处理,它又增加了一层

正如Apple在其文档中所述,由于其内部功能,您不应该在线程之间传递NSManagedObjectContext。默认情况下,所有UI更新都应该在主线程上完成。因此我建议您在使用后台线程方法获取数据后,在主线程上进行保存。作为一般规则,我会尝试遵循这一点,但我不知道您的项目是否需要后台保存

问题病因学 问题来自于在后台线程中实例化容器。但是,当它在app委托中声明为
static
时,只会发生一次初始化,并且它没有在后台线程上初始化,这会干扰它的使用

来自适用于NSManagedObjectContext的apple API:

核心数据使用线程(或序列化队列)限制来保护 托管对象和托管对象上下文(参见核心数据编程 指南)。这样做的结果是,上下文假定为默认值 owner是分配它的线程或队列,这由 调用其init方法的线程。所以你不应该,, 在一个线程上初始化上下文,然后将其传递给另一个线程。 相反,您应该将引用传递给持久存储协调器 并让接收线程/队列创建从 那个如果使用操作,则必须在main中创建上下文(用于 串行队列)或启动(对于并发队列)

核心数据栈的初始化方法
  • 不要在应用程序委托中初始化和设置核心数据堆栈。使用NSObject子类并将其作为核心数据堆栈(代码来自ray wenderlich教程)。如果使用,则应初始化此应用程序内委托,然后将其传递给其他人。但请记住,您的问题源于线程,因此您需要像以前一样使用静态变量,或者使用更推荐的方法,在提取完成并退出后台线程后保存到核心数据:

    class CoreDataStack: NSObject {
        let moduleName = "YourModuleName"
    
        func saveToMainContext() { // Just a helper method for removing boilerplate code when you want to save. Remember this will be done on the main thread if called.
            if objectContext.hasChanges {
                do {
                    try objectContext.save()
                } catch {
                    print("Error saving main ManagedObjectContext: \(error)")
                }
            }
        }
    
        lazy var managedObjectModel: NSManagedObjectModel = {
            let modelURL = Bundle.main.url(forResource: moduleName, withExtension: "momd")!
            return NSManagedObjectModel(contentsOf: modelURL)!
        }()
    
        lazy var applicationDocumentsDirectory: URL = {
            return FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).last!
        }()
    
        lazy var persistentStoreCoordinator: NSPersistentStoreCoordinator = {
            let coordinator = NSPersistentStoreCoordinator(managedObjectModel: self.managedObjectModel)
    
            let persistenStoreURL = self.applicationDocumentsDirectory.appendingPathComponent("\(moduleName).sqlite")
    
            do {
                try coordinator.addPersistentStore(ofType: NSSQLiteStoreType, configurationName: nil, at: persistenStoreURL, options: [NSMigratePersistentStoresAutomaticallyOption: true, NSInferMappingModelAutomaticallyOption : true])
            } catch {
                fatalError("Persistent Store error: \(error)")
            }
            return coordinator
        }()
    
        lazy var objectContext: NSManagedObjectContext = {
            let context = NSManagedObjectContext(concurrencyType: .mainQueueConcurrencyType) // As stated in the documentation change this depending on your need, but i recommend sticking to main thread if possible.
    
            context.persistentStoreCoordinator = self.persistentStoreCoordinator
            return context
        }()
    }
    
  • 将应用程序代理用作安装程序。我通常使用
    (UIApplication.shared.delegate as!AppDelegate)初始化应用程序委托中的对象。persistentContainer
    不是静态对象时,我必须从那里初始化对象,它将引用当前使用的应用程序委托。然而,这可能并不重要

    • 或者,您可以在应用程序代理中使用
      静态
      
      
  • 希望我不会迟到。也许这对其他人有帮助。祝你好运