将NSManagedObjectContext传递给UITabBarController子视图控制器的最佳实践?

时间:2011-12-25 01:08:08

标签: objective-c cocoa-touch core-data

所以在我看来这里有一个Catch-22的情况。请注意以下广泛(且巧妙)持有的应用程序架构立场:

  1. Apple(以及大多数alpha开发人员)建议不要使用单例或访问app delegate singleton来检索NSManagedObjectContext。刚性,设计差等等。好的 - 我同意!
  2. 迭代UITabbarController的子视图控制器并为每个子VC提供对上下文的引用(依赖注入)也是愚蠢的,因为您在应用程序启动时加载应用程序中的每个选项卡,只是为了传递引用。这也违反了Apple推荐的应用程序架构。
  3. 你如何解决这个问题?当视图控制器从nib中唤醒时,是否使用NotificationCenter发布通知,然后在上下文引用中传递app delegate?这是我能想到的第一种方式,只有#1和#2,但这对我来说也是一种扭曲。

    有更优雅的方式吗?

    编辑:初始化视图控制器时执行通知可能是竞争条件,因为如果您正在使用Storyboard,您的tabbar的子视图控制器往往会被初始化(尽管sans-view装载)在发射时。所以你必须在viewDidLoad中做这样的通知,这对于MVC约定来说是一个坏主意。在用户执行与视图相关的任何操作之前,它还会将您的任务与数据模型(例如预缓存性能)相关联。

4 个答案:

答案 0 :(得分:13)

将NSManagedObject实例传递给视图控制器时,该视图控制器可以保留这些对象。然后,它可以通过调用

来通过这些托管对象访问NSManagedObjectContext
-[NSManagedObject managedObjectContext]

我不确定这是否适用于您的特定情况,但通常情况会如此。应用程序委托或根视图控制器创建上下文,然后传递托管对象。

更新

如果你需要在多个地方使用上下文,我发现另一种有用的模式:

子类NSManagedObjectContext

@interface MyManagedObjectContext : NSManagedObjectContext

+ (MyManagedObjectContext *)mainThreadContext;

@end

即。 UI /主线程的 singleton 上下文。这比使用App委托更干净,因为其他类不必访问App委托,但可以直接使用此类。此外,商店和模型的初始化可以封装到这个类中:

@implementation MyManagedObjectContext

+ (MyManagedObjectContext *)mainThreadContext;
{
   static MyManagedObjectContext *moc;
   static dispatch_once_t onceToken;
   dispatch_once(&onceToken, ^{
       moc = [[self alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];
       // Setup persistent store coordinator here
   });
   return moc;
}

@end

答案 1 :(得分:5)

我发现自你发布问题已经过了一段时间,但我有一个不同的方法,我想与你和其他人分享。

我假设您要在所有/部分视图控制器中注入托管对象上下文,这些视图控制器显示为UITabViewController的选项卡,并且您正在使用带有UITabBarController的Storyboard作为rootViewController。

  1. 在AppDelegate标头中,让AppDelegate实现UITabBarControllerDelegate协议。

    @interface AppDelegate : UIResponder <UIApplicationDelegate, UITabBarControllerDelegate>
    ...
  2. 在AppDelegate实现中添加以下UITabBarControllerDelegate方法。它将负责在具有该属性的任何视图控制器中设置托管对象上下文。

    - (BOOL) tabBarController:(UITabBarController *)tabBarController
    shouldSelectViewController:(UIViewController *)viewController {
    if ([viewController respondsToSelector:@selector(setManagedObjectContext:)]) {
        if ([viewController performSelector:@selector(managedObjectContext)] == nil) {
            [viewController performSelector:@selector(setManagedObjectContext:) withObject:self.managedObjectContext];
        }
    }
    return YES;
    }
  3. 在您的应用程序中:didFinishLaunchingWithOptions:将self设置为UITabBarController委托。

    UITabBarController *tabBarController = (UITabBarController *) self.window.rootViewController;
    tabBarController.delegate = self;
  4. 遗憾的是,当时尚未准备好要加载的第一个视图控制器(在tabBarController.selectedViewController中),并且也不会调用该委托。所以设置第一个的最简单方法是观察TabBarController的selectedViewController属性

    [tabBarController addObserver:self forKeyPath:@"selectedViewController"
                          options:NSKeyValueChangeSetting context:nil];

    并将其设置在那里。

    - (void) observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
    [object removeObserver:self forKeyPath:@"selectedViewController"];
    UIViewController *viewController = [change valueForKey:NSKeyValueChangeNewKey];
    if ([viewController respondsToSelector:@selector(setManagedObjectContext:)]) {
        if ([viewController performSelector:@selector(managedObjectContext)] == nil) {
            [viewController performSelector:@selector(setManagedObjectContext:) withObject:self.managedObjectContext];
        }
    }
    }

答案 2 :(得分:1)

我会创建一个核心数据提供程序类,它是一个单例。此类可以只提供持久性存储,以便每个视图控制器可以根据需要创建自己的上下文。或者您可以更像是管理器使用提供程序类并返回新的(如果需要)上下文。它当然也应该为每个上下文设置合并通知,以便您可以监听线程之间的更改。

通过此设置,每个视图控制器都可以向提供者/管理者询问上下文,提供者/管理者将在内部处理所有内容并返回视图控制器的上下文。

您怎么看?

答案 3 :(得分:0)

恕我直言,我发现更优雅的是不在ViewController中进行Core Data繁重的工作。 我建议你将数据操作封装到一个对象中,让对象引用/封装你的Core Data堆栈。

当您的应用程序和数据模型增长时,您希望对传递MOC的方式非常严格。如果您使用并发并且必须处理多个MOC,这变得更加重要。此外,您的ViewControllers只需要一个用于FetchedResultsControllers的MOC。在大多数其他情况下,您可以很好地传递ManagedObjects。