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

Eri*_*erg 21 cocoa-touch core-data objective-c

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

  1. Apple(和大多数alpha开发人员)建议不要使用单例或访问app delegate singleton来检索NSManagedObjectContext.刚性,设计差等等.好的 - 我同意!
  2. 迭代UITabbarController的子视图控制器并为每个子VC提供对上下文的引用(依赖注入)也是愚蠢的,因为您在应用程序启动时加载应用程序中的每个选项卡,只是为了传递引用.这也违背了Apple推荐的应用程序架构.

你是如何解决这个问题的?当视图控制器从nib中唤醒时,是否使用NotificationCenter发布通知,然后在上下文引用中传递app delegate?这是我能想到的第一种方式,只有#1和#2,但这对我来说也是一种扭曲.

有更优雅的方式吗?

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

Dan*_*ert 13

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

-[NSManagedObject managedObjectContext]
Run Code Online (Sandbox Code Playgroud)

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

更新

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

子类NSManagedObjectContext:

@interface MyManagedObjectContext : NSManagedObjectContext

+ (MyManagedObjectContext *)mainThreadContext;

@end
Run Code Online (Sandbox Code Playgroud)

即UI /主线程的单例上下文.这比使用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
Run Code Online (Sandbox Code Playgroud)


Jor*_*tiz 5

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

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

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

    @interface AppDelegate : UIResponder <UIApplicationDelegate, UITabBarControllerDelegate>
    ...
    Run Code Online (Sandbox Code Playgroud)
  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;
    }
    Run Code Online (Sandbox Code Playgroud)
  3. 在您的应用程序中:didFinishLaunchingWithOptions:将self设置为UITabBarController委托.

    UITabBarController *tabBarController = (UITabBarController *) self.window.rootViewController;
    tabBarController.delegate = self;
    Run Code Online (Sandbox Code Playgroud)
  4. 遗憾的是,第一个要加载的视图控制器在那时(in tabBarController.selectedViewController)尚未就绪,并且也不会调用该委托.因此,设置第一个的最简单方法是观察TabBarController的selectedViewController属性

    [tabBarController addObserver:self forKeyPath:@"selectedViewController"
                          options:NSKeyValueChangeSetting context:nil];
    Run Code Online (Sandbox Code Playgroud)

    并把它放在那里.

    - (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];
        }
    }
    }
    Run Code Online (Sandbox Code Playgroud)