核心数据最佳实践 - 我是否在利用 C.D。正确有效? - iOS Swift

Core Data Best Practices - Am I utilizing C.D. Correctly and Efficiently? - iOS Swift

我目前是如何使用核心数据的:

我的应用程序加载:在此页面上,我创建了一个托管对象上下文,获取托管对象,并加载/显示它们。这是一个表视图,所以我允许删除。为了删除,我创建了一个托管对象上下文并删除了托管对象并重新加载了表视图。我的整个应用程序都使用相同的方法,还有其他操作,例如更新等。 要点是我为每个操作创建一个新的托管对象上下文。

我目前对 Core Data 的理解是,托管对象上下文有点像一个队列,其中填满了操作。其中,托管对象是正在修改并放入队列中以进行操作的项目。按照这个逻辑,整个应用不应该只有一个队列吗?

主要问题:

我是否需要在每次操作之前创建托管对象上下文?或者我可以创建一个托管对象上下文,在应用程序委托中说完成启动了吗?并在整个应用程序中使用它?

编辑:

对于未来的观众(如果我理解所提供的答案),我的代码现在大致如下所示:

  1. 我有一个用于 Core Data 的 class,在这个函数中创建了一个 ManagedObjectContext
  2. 这个创建 MOC 的函数在应用程序委托中调用,它成功 returns MOC,然后我将其存储在一个单例中。
  3. 在我的整个应用程序中,因为我需要更改我的核心数据对象,所以我执行以下操作:

    if let managedContext = ShareData.sharedInstance.managedObjectContext { // DO STUFF, update, delete, create ect ect ect }

编辑 2:我可能误解了 "Do not create MOC in App delegate",目前正在研究这个问题并试图了解创建 MOC 的最佳位置。如果您可以启动应用程序的每个视图都需要 MOC,那么在其他地方创建一个似乎很乏味。

在应用程序的整个生命周期中,您实际上只需要创建一个核心数据堆栈。 (这有例外,但对于大多数应用程序)。

我目前正在使用 JSQCoreDataKit 来管理堆栈的创建和保存上下文。绝对值得一看。

处​​理核心数据的正常方法类似于...

  1. 在应用程序启动时创建核心数据堆栈。通常通过单例访问(不在应用程序委托中)。

  2. 为了从核心数据中读取数据,从核心数据堆栈中获取 mainContext 并在此 mainContext.

    上执行提取
  3. 为了写入(添加、更新、删除)数据,您可以使用 mainContext 但也可以从核心数据中获取 backgroundContextchildContext堆。在上下文的 perform 块内执行更新和 saveContext。 (这会将更改合并到主要上下文中供您阅读)。

这应该涵盖了大部分您想做的事情。

看看 JSQCoreDataKit。它使托管对象上下文的创建变得更加简单。

编辑以澄清第 1 点

将内容放入 AppDelegate 是一种非常笨拙和懒惰的全面获取数据的方法。 AppDelegate 是一个单例,所以它看起来是放置它的最佳位置。但随后您添加的越来越多,突然间您拥有一个巨大的应用委托来驱动您的整个应用。

使用单一职责原则,您的应用代理应该做一件事……成为您应用的代理。它应该响应应用程序状态变化等...

我忘了添加...如果您为您的应用创建第二个目标(比如 TVOS 目标)。它不会使用相同的 AppDelegate。如果您所有的 CoreData 代码(和其他代码)都在 AppDelegate 中,那么 TVOS 应用程序将无法访问它。将它放在两个应用程序都可以访问的另一个 class 中意味着这两个应用程序可以共享您用于 CoreData(等)的代码。

创建另一个保存核心数据堆栈的文件并在您第一次需要访问核心数据时启动它非常容易。 (不一定来自 AppDelegate,但首先你需要做一个 read/write)。

RE 将核心数据堆栈设置放置在初始视图控制器中。你可以那样做。然后,您会遇到如何从应用程序中的每个其他视图控制器获取该核心数据堆栈的问题。您可以将初始视图控制器设为单例(不要这样做),也可以传递堆栈。

两种方法都可以,但 CoreData 本质上是一个单例。您的 phone 的光盘上只有一组数据。所以在这里创建一个单例并不是一件坏事。

如果你确实制作了一个单例,那么就把它做成一个纯粹的核心数据堆栈单例。我有一个叫 CoreDataStackManager 的东西。它所做的只是保持 coreDataStack 属性.

每次要对数据库执行某些操作时,都无需创建管理器对象上下文。最佳做法是创建一个包含 MOC 的 class,您可以将所有可能的数据库操作集中在其中。这个 class 将成为您的数据访问管理员

这里有一个你可能看起来像的例子:

import Foundation
import CoreData
enum CommitError: Error {
    case failureToSave(error:Error?)
}
class CoreDataManager: NSObject {
    var managedObjectContext: NSManagedObjectContext
    override init() {
        // This resource is the same name as your xcdatamodeld contained in your project.
    guard let modelURL = Bundle.main.url(forResource: "YOUR_DATABASE_NAME", withExtension:"momd") else {
        fatalError("Error loading model from bundle")
    }
    // The managed object model for the application. It is a fatal error for the application not to be able to find and load its model.
    guard let mom = NSManagedObjectModel(contentsOf: modelURL) else {
        fatalError("Error initializing mom from: \(modelURL)")
    }
    let psc = NSPersistentStoreCoordinator(managedObjectModel: mom)
    managedObjectContext = NSManagedObjectContext(concurrencyType: .mainQueueConcurrencyType)
    managedObjectContext.persistentStoreCoordinator = psc
    let urls = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)
    let docURL = urls[urls.endIndex-1]
    /* The directory the application uses to store the Core Data store file.
     This code uses a file named "DataModel.sqlite" in the application's documents directory.
     */
    let storeURL = docURL.appendingPathComponent("YOUR_DATABASE_NAME.sqlite")
    do {
        let options = [NSMigratePersistentStoresAutomaticallyOption: true, NSInferMappingModelAutomaticallyOption: true, NSSQLitePragmasOption: ["journal_mode": "DELETE"]] as [String : Any]
        try psc.addPersistentStore(ofType: NSSQLiteStoreType, configurationName: nil, at: storeURL, options: options)
    } catch {
        fatalError("Error migrating store: \(error)")
    }
}

func commitChanges() throws{
    if self.managedObjectContext.hasChanges{
        do {
            try self.managedObjectContext.save()
        } catch {
            throw CommitError.failureToSave(error: error)
        }
    }
}

func createObject(_ entityName:String) -> NSManagedObject? {

    let result:NSManagedObject? = NSEntityDescription.insertNewObject(forEntityName: entityName, into: self.managedObjectContext)
    return result
}

func deleteEntity(_ entity:NSManagedObject){
    self.managedObjectContext.delete(entity)
}
}

您可以添加其他功能来搜索或创建对象。

如果这解决了您的问题,请告诉我;)

我觉得有必要在 Fogmeister 的回答中添加少数报告:

  1. 在最新的SDK中,Core Data给我们提供了NSPersistentContainer,绝对值得研究。您并不总是需要添加第三方依赖项来管理核心数据。我通常在第一个可见的视图控制器中创建设置,然后根据需要将该实例注入其他视图控制器。不需要单例;如果您可以使用 persistentContainer 配置 VC,那么测试起来会容易得多。
  2. 使用此持久存储的 viewContext 获取请求。这个 运行 在主队列上,适合驱动 UI.
  3. 可以通过调用 newBackgroundContext 向 persistentContainer 请求后台上下文。您可以在此队列上执行任何导入。当您保存它时,更改会自动传播到 viewContext。或者,使用容器的 performBackgroundTask() 方法进行闭包,这将在为您创建的后台队列上 运行。

编辑添加

这是一个通过 NSPersistentContainerperformBackgroundTask 方法在后台队列中保存数据的非常基本的示例。可以从Github下载:https://github.com/Abizern/so-41984004