如何通过 Interface Builder 的 XIB 传递 NSManagedObjectContext

Posted

技术标签:

【中文标题】如何通过 Interface Builder 的 XIB 传递 NSManagedObjectContext【英文标题】:How to pass NSManagedObjectContext via Interface Builder's XIBs 【发布时间】:2011-03-03 18:18:05 【问题描述】:

我有一个简单的 ios 应用程序,在 UINavigationController 下有一个 UIViewController。 UIViewController 有一个用于 NSManagedObjectContext 的 IBOutlet。

AppDelegate 有一个用于导航控制器的 IBOutlet,但没有用于视图控制器。视图控制器在 XIB 进程中自动实例化(作为导航控制器的子进程)。

使用此设置,如何干净地将应用程序委托的 NSManagedObjectContext 分配或传递给视图控制器的 IBOutlet 属性。有一个导航控制器 :) 并且应用程序委托没有 UIViewController 的直接属性。

这是一个奇怪的问题,我想将一个 XIB 组件的属性链接到另一个组件的属性。我所做的大部分 XIB 工作都需要一个属性并将其指向 XIB 中的一个对象,而该对象又会在正常过程中被实例化,但在这种情况下,上下文是在应用程序委托中正确创建的,我只是想要在实例化它时将其传递给视图控制器。

【问题讨论】:

我认为还值得考虑一个有 9 个视图控制器的应用程序 - 在不同的时间使用 - 所有这些都需要访问托管上下文。在某些情况下,视图 A 和视图 C 需要上下文,而视图 B 不需要。在实例化时将其简单地传递给每个视图控制器的幼稚方法似乎很混乱。这是否意味着 view-A 使用上下文创建 view-B(即使 view-B 不需要它),以便 view-B 可以创建 view-C 并传递它?我发现这让我的愚蠢视图控制器太聪明了,而且应用程序不够灵活。 【参考方案1】:

您不需要传递它,只需根据需要从应用代理中获取它:

#import "MyAppDleegate.h"

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

【讨论】:

这是一种方法(确实是一种非常实用的方法),但在一定程度上,它将上下文硬编码到视图本身中。我喜欢它颠倒了设置,但作为一个单例,它违反了“依赖注入”的原则。例如,很难对这个特定的视图控制器进行单独的单元测试,因为它会不断地向 UIApplication 和 AppDelegate 寻求它需要的资源。理想的解决方案是在运行时向视图控制器“提供”上下文,诀窍在于将其连接到 ala IB。 True - 在一些有多个线程访问托管对象的应用程序中,我在 NSManagedObjectContext 上使用了一个类别,它根据需要创建 mocs,使用线程名称作为标识符 - 然后将它们存储在一个以线程名称为键的字典。显然,如果您收到来自未命名线程的请求,则必须命名所有线程并断言。这是关于使用 IB 让我感到烦恼的最后一件事,我喜欢 XCode 4 现在建立连接的方式,但不得不发现一些技巧来做诸如传递 moc 之类的事情,这很令人恼火。【参考方案2】:

Apple 的文档建议您将对托管对象上下文的引用传递给需要它们的类,而不是从您的应用委托中引用它。

这是application:didFinishLaunchingWithOptions: 在我的一个核心数据项目中的样子。

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
        
    LocationsViewController *lvc = (LocationsViewController *)self.navigationController.topViewController;
    lvc.managedObjectContext = self.managedObjectContext;
    assert(lvc.managedObjectContext != nil);
    [self.window addSubview:self.navigationController.view];
    [self.window makeKeyAndVisible];

    return YES;

您会看到,我也是从带有单个根视图控制器的 UINavigationController 开始的。

【讨论】:

谢谢马克。不幸的是,我的应用委托中没有对主视图控制器的引用——只有导航控制器。 但是您已经在 Interface Builder 中将主视图控制器设置为导航控制器的根视图控制器。只需要求导航控制器返回它的顶视图控制器。在视图控制器上为 NSManagedObjectContext 设置公共属性。使用 AppDelegate 中的self.managedObjectContext 在视图控制器上设置managedObjectContext 属性。 是的,我可以做到这一点 - 但正在寻找一种在 Interface Builder 的 XIB 中做到这一点的方法。我还有 9 个不在导航堆栈上的视图控制器,它们也需要托管对象上下文,我也想将它应用到这些视图控制器上。 不幸的是,我也无法在 IB 中将其连接起来。我只知道 Apple 建议传递参考资料一定是有原因的。【参考方案3】:

你的想法是对的,但你正在努力解决的问题似乎完全是你自己创造的。您说您的应用程序委托有一个用于导航控制器的出口,但没有用于导航控制器的根视图控制器,因为您已经设置了 nib,以便在加载 nib 时创建视图控制器。这并没有错,但也没有理由应用程序委托不应该为该控制器提供出口。实际上,使用 outlet 的全部原因是获取对从 nib 加载的内容的引用。

为你的根视图控制器添加一个出口到你的应用委托,并连接它。然后,应用委托可以为控制器提供对托管对象上下文的引用。

关于您关于多个视图控制器的问题,我想知道什么样的实际应用程序可能具有需要数据的视图控制器 (A),加载另一个不需要任何数据的视图控制器 (B),然后第三个(C)又需要数据?一个现实的例子可能会有所帮助,如果你有的话。

请记住,您不必将整个托管对象上下文传递给每个连续的视图控制器。相反,您可以通过传递托管对象来传递控制器完成其工作所需的模型部分。

【讨论】:

以上是关于如何通过 Interface Builder 的 XIB 传递 NSManagedObjectContext的主要内容,如果未能解决你的问题,请参考以下文章

如何通过 Interface Builder 在 Objective-C 中实现搜索栏

如何通过编写 Objective-C 代码来动态更改 Interface Builder xib 文件?

如何在不通过 Interface Builder 获取 UIButton 类的背景的情况下添加图像?

如何在 Xcode Interface Builder 中显示占位符背景?

如何通过读取“[MyClass class]”的行来修复“Interface Builder 文件中的未知类 <MyClass>”错误?

如何像 Interface Builder 一样调整 iOS UI 组件的大小