【问题标题】:Where to place the "Core Data Stack" in a Cocoa/Cocoa Touch application在 Cocoa/Cocoa Touch 应用程序中放置“核心数据堆栈”的位置
【发布时间】:2010-11-19 01:06:00
【问题描述】:

在 iPhone Core Data Template 中,Apple 将 Core Data Stack 放置在 App Delegate 中。

然而,我最初的想法是将此代码移到它自己的类中,该类的职责是处理核心数据堆栈的管理。

您通常将此功能封装在其自己的类中还是将其保留在 App Delegate 中?

【问题讨论】:

    标签: iphone cocoa cocoa-touch core-data


    【解决方案1】:

    总结:无需创建单例来管理Core Data栈;确实这样做可能会适得其反。

    Core Data 堆栈恰好是由应用程序委托创建的。然而,重要的是,正如所有示例所示,堆栈(主要是托管对象上下文)直接从堆栈中检索 (*)。相反,上下文被传递给第一个视图控制器,并从它们在上下文或托管对象中从一个视图控制器传递到下一个视图控制器(如Accessing the Core Data Stack 中所述)。这遵循 iPhone 所有应用程序的基本模式:您将数据或模型控制器从一个视图控制器传递到下一个视图控制器。

    这里描述的单例的典型作用是作为模型控制器。使用 Core Data,托管对象上下文已经是一个模型控制器。如果需要,它还使您能够访问堆栈的其他部分。此外,在某些情况下(如文档中所述),您可能希望使用不同的上下文来执行一组离散的操作。因此,视图控制器的适当货币单位通常是托管对象上下文,否则是托管对象。使用和传递管理堆栈(并从中检索上下文)的单例对象通常最多会引入不必要的间接级别,最坏的情况会引入不必要的应用程序僵化。

    (*) 没有示例使用以下方法检索上下文:

    [[UIApplication delegate] managedObjectContext];
    

    【讨论】:

    • 当我刚开始使用 Core Data 时,不使用依赖注入绝对是一个糟糕的设计。最近,我采取了与您概述的方法相同的方法。主要区别在于,我将核心数据堆栈代码放在 NSManagedObject 上下文中的一个类别中,只是为了在逻辑上将核心数据堆栈代码与 AppDelegate 分开。理论上,我可以像单例一样使用该类别,但我选择不这样做,因为它引入了您所说的“应用程序刚性”。此外,我为 Core Data 堆栈使用了一些自定义代码,这使我可以轻松地将这些代码放入新项目中。
    • 我支持您使用 App Delegate 创建核心数据堆栈。我使用 UITabBarController 作为我的根视图控制器,但我不确定如何将上下文传播到该控制器对象,因为它位于 MainWindow.xib 中,并且我不确定如何为其分配指向 ManagedObjectContext 的指针。 . 我想我为此发布了一个单独的问题。
    • 那份苹果文档说,“当你创建一个视图控制器时,你将它应该使用的上下文传递给它。”但我没有看到这是如何完成的。主视图控制器是通过情节提要创建的,如果使用情节提要,对吗?那么如何传递上下文呢?
    • @VictorEngel 那么,如果视图控制器是由情节提要创建的,您是否发现应该如何传递上下文?令人沮丧的是,到处有人说不要从应用程序委托中获取它,但没有说你应该如何获取上下文?
    • 如果你看一下 Apple 的模板,视图控制器有一个可变的上下文属性。第一个可以使用上下文的视图控制器在 didFinishLaunchingWithOptions 中设置。从那时起,它被传递给每个后续的视图控制器。这也包含在文档中。
    【解决方案2】:

    我有一个单例类,我让它来管理我的核心数据,我不会把它留在应用程序委托上。我宁愿不使用我可能需要的方法来混淆应用程序委托类,例如获取某些对象等

    【讨论】:

    • 对我来说听起来很实用。我很惊讶苹果将它包含在应用程序委托中。
    • 他们这样做可能是因为他们想展示如何做到这一点,而这就是他们尽管放置它的地方很方便,因为应用程序委托已经有点像单身了
    • 拥有一个单例核心数据控制器对象是完全有意义的。我们将其抽象化,以便可以在每个项目中重用它。 +1
    • 我现在也为 Core Data 堆栈使用了一个单例类。我认为它的作用类似于通知中心或共享用户默认值,您可以在其中调用 [[DatabaseController sharedDatabaseController] writableManagedObjectContext] 以在需要时获取特定上下文。回调应用程序委托来获取堆栈似乎很笨重。
    • 我同意 (a) 有一个通用的核心数据管理类,它更容易放入项目(尤其是已经存在的项目)和 (b) 它总是在 AppDelegate 中的原因是他们正试图尽可能多地减少非示例代码 - 那么当 AppDelegate 以这种方式免费运行时(就代码长度而言),为什么要制作一个完整的单例。我会把它放在一个单例中,这样只有处理 Core Data 的类与单例有任何联系,这也意味着更少的类也必须包含 App Delegate 标头。
    【解决方案3】:

    我将核心数据逻辑留在 App 委托中,原因如下:

    1) 我认为将这段代码移到其他类中没有任何真正的优势:委托的概念完全由 App 委托处理的核心数据逻辑实现,因为核心数据模型实际上是你的申请;

    2) 在我见过的所有示例代码中,包括 Apple 示例,核心数据都是由 App 委托处理的;

    3) 即使在 Core Data 书籍中,让 App 委托处理与核心数据相关的代码也是常见的做法;

    4) 就我个人而言,我认为核心数据的临时类实际上并没有提高可读性或其他任何东西,但这是个人喜好问题,我不会在这里争论哪种方法是最好的。对我来说,保持功能的同时保持简单很重要。

    【讨论】:

    • 我通常也会在 App Delegate 中看到 Core Data 堆栈。但是,我查看的代码通常是为了说明目的而创建的。实现某些东西的实际方法有时与此类示例不同。我不想在没有充分理由的情况下盲目地遵循 Apple 的示例代码。我倾向于认为您认为这只是个人喜好问题,无论哪种方式都有一些优势是正确的。
    • 我还认为参数 2 和 3 是因为在教程或示例中,您试图尽可能减少与您尝试呈现的内容无关的任何代码 - 因此实现单例给应该是一个简单的例子增加了太多的开销。我不喜欢将这些东西保留在 App Delegate 中的一点是,它增加了必须了解的关于 App Delegate 的事情的数量......
    • " 委托的概念完全由 App 委托处理的核心数据逻辑实现,因为核心数据模型实际上是应用程序的基本部分;"不,UIApplication 没有将 Core Data 功能的任何责任委托给它的委托人。您可能会认为持久存储是应用程序级别的问题,但它不是 UIApplicationDelegate 的一部分。
    【解决方案4】:

    就您而言,我会问自己的问题是“Core Data 堆栈‘属于’谁?”数据本身真的是应用程序的一部分,不是吗? (Mac 上的 C.F. Core Data,您可能有一个应用程序能够同时处理多个文档,因此 Core Data 堆栈属于每个文档。)

    在任何 Cocoa/Cocoa Touch 应用程序中,App Delegate 通常是自定义应用程序行为的首选方式,因此这是 Core Data 堆栈的自然位置。

    现在,我怀疑你遇到的问题是,不断地写这样的东西感觉不对:

    NSManagedObjectContext *context = [(MyAppDelegate *)[[UIApplication sharedApplication] delegate] managedObjectContext];
    

    在这些情况下,我通常会写这样的函数(而不是方法):

    NSManagedObjectContext *UIAppManagedObjectContext() {
        return [*(MyAppDelegate *)[[UIApplication sharedApplication] delegate] managedObjectContext];
    }
    

    我为NSPersistentStoreCoordinatorNSManagedObjectModel 编写了一个类似的函数。我将所有这些都放在 App Delegate 的 .h/.m 文件中,因为这些也是应用程序级对象。

    【讨论】:

    • 这很有趣。这正是我不喜欢的一段代码。我不喜欢跑到 App Delegate 来获取文件存储信息。感觉“不对劲”。这让我质疑其他开发人员是如何处理这种情况的。
    • 第一个代码 sn-p 感觉不对的原因是因为它有代码味道。将其包装在一个方便的功能中只是除臭剂。将上下文简单地传递给需要它的对象(主要使用属性注入)要简单得多。
    • 您应该像这样从应用程序委托中获取上下文。您应该将上下文从一个视图控制器传递到下一个视图控制器,如所有 Apple 示例中所示。
    • 更多反对使用应用代理分发数据模型的支持证据:hollance.com/2012/02/dont-abuse-the-app-delegate
    【解决方案5】:

    我将在新答案中列出这一点。 (我已经放弃了我以前的 FJSCoreDataStack 课程来支持这个)

    我处理这个问题的新方法是在 NSManagedObjectContext 上使用一个类别。我添加了以下类方法:

    + (NSManagedObjectContext *)defaultManagedObjectContext;
    + (NSManagedObjectContext *)scratchpadManagedObjectContext;
    + (NSManagedObjectModel *)managedObjectModel;
    + (NSPersistentStoreCoordinator *)persistentStoreCoordinator;
    + (NSString *)applicationDocumentsDirectory;
    

    这可以让我的应用程序委托之外的所有内容,并在我选择使用它时提供单例访问。但是,我仍然使用来自 App Delegate 的依赖注入(正如 mmalc 所说,它在我的代码中引入了不灵活)。我只是将所有“核心数据堆栈”代码移到 NSManagedObjectCONtext 类别中。

    我喜欢传递引用,特别是因为我有一个很好的“便签本上下文”方法。这使我的视图控制器保持灵活,因为我没有将它们提交给“defaultManagedObjectContext”。

    也与 iPhone 世界中的对话相关(并且可能与您的架构有关): NSFetchedResultsController and constructing NSFetchRequests

    【讨论】:

      【解决方案6】:

      我赞成让应用委托知道模型从哪里开始,让模型知道托管对象上下文在哪里。模型的核心数据——对我来说似乎是模型的实现细节,控制器类(如应用程序委托)应该只问“给我关于模型的信息”,模型应该知道如何回答那个问题。因此,通过控制器对象提供 Core Data 对象似乎是一种泄漏抽象。

      【讨论】:

      • 在 iPhone 开发中成为问题的是使用和配置 NSFetchedResultsControllers。您现在还可以让您的“模型”如何配置和返回 NSFetcheResultsControllers,但看起来模型类会变得有点臃肿。我觉得 NSFetchedResultsControllers 模糊了控制器和模型代码之间的界限(不一定是坏的方式)。我最近将这个和其他一些想法纳入我的新配置(添加了新答案)。
      • 我同意@Graham,这就是我的做法。您的UIViewControllers 不需要与NSManagedObjectContext 混淆,他们应该与模型交谈并询问他们需要什么。获取该信息的机制与我的视图控制器无关。
      猜你喜欢
      • 2011-07-13
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-12-15
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多