Mar*_*aka 4 core-data ios swift
当我们在选中 Core Data 选项的情况下在 Xcode 中创建一个新项目时,它会在 AppDelegate.swift 上生成一个定义 Core Data 堆栈的新项目:
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)")
}
}
}
}
Run Code Online (Sandbox Code Playgroud)
为了方便我访问persistentContainer,我还添加了这段代码:
static var persistentContainer: NSPersistentContainer {
guard let appDelegate = UIApplication.shared.delegate as? AppDelegate else { fatalError("Could not convert delegate to AppDelegate") }
return appDelegate.persistentContainer
}
Run Code Online (Sandbox Code Playgroud)
所以我可以这样称呼它:
let container = AppDelegate.persistentContainer
Run Code Online (Sandbox Code Playgroud)
当我尝试从后台线程访问它时会出现问题。例如,我有一段代码在后台运行并从 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...
})
}
}
Run Code Online (Sandbox Code Playgroud)
当我尝试获取持久容器时,它会在控制台上生成:
Main Thread Checker: UI API called on a background thread: -[UIApplication delegate]
Run Code Online (Sandbox Code Playgroud)
为了不再发生这种情况,我已将我persistentContainer从lazy var改为staticon AppDelegate:
static var persistentContainer: NSPersistentContainer = {
// same code as before...
}()
Run Code Online (Sandbox Code Playgroud)
并且错误不再发生。
但我想知道这是否会产生任何我不知道的副作用。我的意思是,persistentContainer无论如何我都只有一个,因为只有一个实例AppDelegate对吗?所以我可以像我一样将它更改为静态并AppDelegate.persistentContainer在我的应用程序的其他部分使用它来访问它而没有任何问题?
或者是否有另一种推荐的模式来处理persistentContainer实例化和使用?
你好。我自己正在处理应用程序的核心数据,我发现有几种方法可以处理核心数据,并且通过线程处理它增加了另一层。
正如 Apple 在其文档中所述,由于其内部功能,您不应在线程之间传递 NSManagedObjectContext。默认情况下,所有 UI 更新都应在主线程上完成。因此,我建议您在使用后台线程方法获取数据后,在主线程上进行保存。作为一般规则,我会尝试遵循这一点,但我不知道您的项目是否需要后台保存?
问题来自您在后台线程中实例化容器。但是,当它static在应用程序委托中声明时,只会发生一次初始化,并且不会在后台线程上初始化,这会干扰其使用。
来自 Apple API for NSManagedObjectContext Apple API 网站:
Core Data 使用线程(或序列化队列)限制来保护托管对象和托管对象上下文(请参阅 Core Data 编程指南)。这样做的结果是,上下文假定默认所有者是分配它的线程或队列——这由调用其 init 方法的线程确定。因此,您不应在一个线程上初始化上下文,然后将其传递给另一个线程。相反,您应该传递对持久存储协调器的引用,并让接收线程/队列创建一个从中派生的新上下文。如果使用 Operation,则必须在 main(对于串行队列)或 start(对于并发队列)中创建上下文。
不要在应用程序委托中初始化和设置您的核心数据堆栈。使用 NSObject 子类并将其作为核心数据堆栈(来自 ray wenderlich 教程的代码。Ray Wenderlich 教程(1 岁))。如果使用,您应该在应用程序委托中初始化它,然后将其传递。但请记住,您的问题源于线程,因此您需要像以前一样使用静态变量或更推荐的方式,在提取完成并退出后台线程后保存到核心数据。:
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
}()
}
Run Code Online (Sandbox Code Playgroud)使用应用程序委托作为设置。我通常从应用程序委托初始化对象,(UIApplication.shared.delegate as! AppDelegate).persistentContainer当它们不是静态时,我必须从那里初始化,这将引用当前使用的应用程序委托。然而,这可能无关紧要。
static在应用程序委托中使用。希望我不会为此迟到。也许它可以帮助其他人。祝你好运。
| 归档时间: |
|
| 查看次数: |
4288 次 |
| 最近记录: |