为啥从 UIApplicationDelegate 获取 ManagedObjectContext 的 Apple 文档不好?

Posted

技术标签:

【中文标题】为啥从 UIApplicationDelegate 获取 ManagedObjectContext 的 Apple 文档不好?【英文标题】:Why does Apple documentation that getting ManagedObjectContext from UIApplicationDelegate is bad?为什么从 UIApplicationDelegate 获取 ManagedObjectContext 的 Apple 文档不好? 【发布时间】:2011-07-31 04:12:44 【问题描述】:

只是好奇为什么 ManagedObjectContexts 应该在创建时传递给 UIViewControllers,而不是仅仅从 UIApplicationDelegate 中获取?

文档说这会使您的应用程序更加僵化,但我看不出何时使用哪种模式的细微差别。

谢谢!

【问题讨论】:

【参考方案1】:

想象一下,我请你做一些任务,比如粉刷一个房间。如果我只是告诉你“去粉刷一个房间”,你需要问我很多问题,比如:

哪个房间? 油漆在哪里? 画笔在哪里? 我应该使用滴布吗?

简而言之,如果没有我的帮助,您将无法完成任务。如果你每次都要依靠我,你就不会是一个很灵活的画家。解决这个问题的一种方法是让我在一开始就给你所有你需要的东西。而不是“去粉刷一个房间”,我会说“请用这桶油漆和这把刷子粉刷 348 号房间,不要用抹布。”现在,你已经拥有了你需要的一切,你可以在没有我进一步帮助的情况下开始工作。你是一个灵活得多的工人,因为你不再依赖我。

同样的事情也适用于视图控制器(以及一般的对象);最好给他们所需的一切,而不是让他们依赖于像应用程序委托这样的特定对象。这不仅适用于托管对象上下文,而且适用于他们完成工作所需的任何信息。

【讨论】:

【参考方案2】:

这主要是因为您希望将dependency injection 与您的 UIViewController 一起使用,而不是仅仅从 UIApplication 中获取所有内容,这样可以使您的委托保持干净而不是充满参考黑客。

这也是为了符合 MVC 模式:

    型号

    视图控制器(仅用于视图逻辑)

    控制器(用于视图和模型之间的协调)

【讨论】:

【参考方案3】:

我倾向于不同意这种模式。

首先,我尝试将 Core Data 视为一个实现细节,并且作为任何实现细节,它应该隐藏在一个好的外观之后。外观是我为模型对象公开的接口。例如,如果我有两个模型对象; CourceStudent,任何课程都可以有多个学生。我不想让控制器承担设置谓词和排序描述符的职责,并跳过所有 Core Data 箍来获取特定班级的学生列表。有一种完全有效的方法可以在模型中公开它:

@interface Cource (StudentAccess)
-(NSArray*)studentsStortedByName;
@end

然后在 Model 类中一劳永逸地实现丑陋的东西。隐藏 Core Data 的所有复杂细节,无需传递托管对象上下文。但是我该如何找到来源,它必须从正确的地方开始?是的,确实如此,但您无需将其暴露给控制器。添加这样的方法也是非常合理的:

@interface Cource (CourceAccess)
+(Cource*)caurceByID:(NSString*)courceID;
+(NSArray*)allCources;
+(NSArray*)courcesHeldByTeacher:(Teacher*)teacher;
@end

这也有助于最小化控制器之间的依赖关系。并减少模型和控制器之间的依赖关系。假设我有一个CourceViewController 和一个StudenViewController,我没有将核心数据细节隐藏在外观后面,并且也想传递托管对象上下文,那么我最终会得到一个这样的指定初始化程序:

 -(id)initWithManagedObjectContext:(NSManagedObjectContext*)moc
                           student:(Student*)student;

虽然有一个好的外观,但我最终得到了这个:

 -(id)initWithStudent:(Student*)student;

最小化外观背后的依赖,支持依赖注入也使得更改内部实现变得更加容易。传递托管对象上下文鼓励每个控制器为基本内容实现自己的逻辑。以studentsSortedByName 方法为例。起初它可能是按姓/名排序的,如果后来更改为姓/名排序,您将不得不去每个对学生进行排序的控制器并进行更改。一个好的外观方法需要您更改一种方法,并且所有控制器都会自动免费获得更新。

【讨论】:

我看不出你在哪里不同意苹果——你只是更进一步并隐藏了上下文。您不会主张让您的视图控制器从应用程序委托或其他单例中获取学生或课程列表,对吗? @Caleb - 不是来自应用程序委托,但我不会因为从Course 类的类方法中提取课程列表而感到羞耻。将类视为单例。 我不同意排序和谓词属于模型层。只有接口需要排序和谓词,所以它们属于控制器。在模型中放置排序和谓词会将模型与特定接口联系起来,并破坏模型对应用处理的真实世界对象、事件或条件的模拟的逻辑完整性。 @TechZen - 只要您的应用仅以一种方式排序,就在模型中保持排序。当应用程序以多种方式显示排序广告选项时,请不要移动排序。否则你最终会得到胖控制器和大量复制粘贴代码。 @Peylow -- 模型应该只处理数据建模。它应该是抽象的并且可以与任何接口一起使用。排序不是模型逻辑的一部分,仅用于显示。 可以想象,一个模型可以显示为具有实体属性以及多种可能的所有排列组合的尽可能多的种类。 你不能将这么多种类塞进一个模型中而仍然保留任何抽象、模块化和可移植性。【参考方案4】:

Apple Docs 试图培养最广泛适用和可持续的设计模式。 依赖注入是首选,因为它允许最灵活、可扩展、可重用和可维护的设计。

随着应用程序复杂性的增加,使用准单例(如将上下文停放在应用程序委托中)会出现问题。在更复杂的应用程序中,您可能会将多个上下文关联到多个商店。您可能希望相同的视图控制器/视图对在不同时间显示来自不同上下文的数据,或者您最终可能会在不同线程/操作上获得多个上下文。您不能将所有这些上下文堆积在应用程序委托中。

如果您有一个具有单一上下文的简单应用程序,那么将准单例与应用程序委托一起使用可以很好地工作。我过去曾在几个较小的应用程序上使用过它,但没有立即出现问题,但是当应用程序超时增长时,我确实在几个应用程序上遇到了可伸缩性问题。

使用哪种模式取决于您的运输限制以及您对进化应用在整个生命周期内的最佳猜测。如果它是一个小型的一次性应用程序,那么应用程序委托准单例就可以正常工作。如果应用程序更复杂,可能会变得更复杂,或者可能会产生其他相关的应用程序来重用现有组件,那么依赖注入就是要走的路。

【讨论】:

以上是关于为啥从 UIApplicationDelegate 获取 ManagedObjectContext 的 Apple 文档不好?的主要内容,如果未能解决你的问题,请参考以下文章

如何在命令行 GHUnit 中使用 UIApplicationDelegate?

尝试设置窗口出口时,从两个 UIApplicationDelegate 实现中提取公共超类会导致崩溃

如何从 UIApplicationDelegate 实例“重新启动”iOS 应用程序/重新实例化初始视图控制器?

如何将 UIApplicationDelegate 添加到 UIResponder 链的末尾?

UIApplicationDelegate 在 Swift 应用程序中没有名为 managedObjectContext 的成员

扩展 UIApplicationDelegate 协议