所以在我看来这里有一个Catch-22的情况。请注意以下广泛(且巧妙)持有的应用程序架构立场:
你如何解决这个问题?当视图控制器从nib中唤醒时,是否使用NotificationCenter发布通知,然后在上下文引用中传递app delegate?这是我能想到的第一种方式,只有#1和#2,但这对我来说也是一种扭曲。
有更优雅的方式吗?
编辑:初始化视图控制器时执行通知可能是竞争条件,因为如果您正在使用Storyboard,您的tabbar的子视图控制器往往会被初始化(尽管sans-view装载)在发射时。所以你必须在viewDidLoad中做这样的通知,这对于MVC约定来说是一个坏主意。在用户执行与视图相关的任何操作之前,它还会将您的任务与数据模型(例如预缓存性能)相关联。
答案 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。
在AppDelegate标头中,让AppDelegate实现UITabBarControllerDelegate协议。
@interface AppDelegate : UIResponder <UIApplicationDelegate, UITabBarControllerDelegate>
...
在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;
}
在您的应用程序中:didFinishLaunchingWithOptions:将self设置为UITabBarController委托。
UITabBarController *tabBarController = (UITabBarController *) self.window.rootViewController;
tabBarController.delegate = self;
遗憾的是,当时尚未准备好要加载的第一个视图控制器(在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。