Ios 处理AppDelegate上persistentContainer实例的正确方法
当我们在选中Core Data选项的Xcode中创建新项目时,它会在AppDelegate.swift上生成一个定义核心数据堆栈的新项目: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
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中创建上下文(用于
串行队列)或启动(对于并发队列)
核心数据栈的初始化方法
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
不是静态对象时,我必须从那里初始化对象,它将引用当前使用的应用程序委托。然而,这可能并不重要
- 或者,您可以在应用程序代理中使用
静态
李>