Передача ManagedObjectContext для просмотра контроллеров с использованием раскадровок с корневым UITabBarController
Используя раскадровки, у вас нет простого доступа к первому контроллеру представления в appDelegate (хотя, как только вы это сделаете prepareForSegue
позволяет легко передавать ManagedObjectContext вниз по стеку навигации.
Я остановился на предоставлении каждому контроллеру представления (или суперклассу каждого контроллера представления), требующему Core Data, доступа к члену moc:
@synthesize moc = _moc;
@property (nonatomic) __weak NSManagedObjectContext *moc;
Меня это беспокоит, потому что это не очень элегантный способ сделать это - слишком много кода. Но прямое назначение требует указания абсолютных индексов в массивах viewControllers и изменения appDelegate каждый раз, когда изменяется требование для ManagedObjectContexts
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
UITabBarController *tabBarController = (UITabBarController *)self.window.rootViewController;
// rootView gets a tab bar controller
for(UINavigationController *navController in tabBarController.viewControllers) {
for(UIViewController *viewController in navController.viewControllers) {
if([viewController respondsToSelector:@selector(setMoc:)]) {
[viewController performSelector:@selector(setMoc:) withObject:self.managedObjectContext];
NSLog(@"Passed moc to %@", [viewController description]);
}
}
}
return YES;
}
Каковы недостатки этого подхода и есть ли лучший способ? Лучше попробовать и быть более общим:
- (void)assignManagedObjectContextIfResponds:(UIViewController *)viewController {
if([viewController respondsToSelector:@selector(setMoc:)]) {
[viewController performSelector:@selector(setMoc:) withObject:self.managedObjectContext];
NSLog(@"Passed moc to %@", [viewController description]);
}
}
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
NSMutableArray *viewControllers = [NSMutableArray array];
UIViewController *firstLevelViewController = self.window.rootViewController;
if([firstLevelViewController respondsToSelector:@selector(viewControllers)]) {
NSArray *firstLevelViewControllers = [firstLevelViewController performSelector:@selector(viewControllers)];
for(UIViewController *secondLevelViewController in firstLevelViewControllers) {
if([secondLevelViewController respondsToSelector:@selector(viewControllers)]) {
NSArray *secondLevelViewControllers = [secondLevelViewController performSelector:@selector(viewControllers)];
for(UIViewController *thirdLevelViewController in secondLevelViewControllers) {
[viewControllers addObject:thirdLevelViewController];
}
} else {
[viewControllers addObject:secondLevelViewController];
}
}
} else {
// this is the simple case, just one view controller as root
[viewControllers addObject:firstLevelViewController];
}
// iterate over all the collected top-level view controllers and assign moc to them if they respond
for(UIViewController *viewController in viewControllers) {
[self assignManagedObjectContextIfResponds:viewController];
}
return YES;
}
2 ответа
Адам,
Пока я изучал раскадровки, я в основном делал то же самое, что и вы, за исключением того, что каждый из моих контроллеров представления со свойством MOC соответствовал протоколу.
Там нет ничего существенно другого, поэтому я буду двигаться дальше.
Я думаю, дело в том, что раскадровки, IMO, наполовину испечены. Исходя из.Net фона, чего явно не хватает, это инфраструктура построителя объектов в сочетании с контейнером IoC. Когда Apple добавит, что раскадровки будут потрясающими. Когда среда раскадровки сможет взглянуть на destinationViewController, определить его зависимости и разрешить их из жизни контейнера, будет здорово. На данный момент все, что он действительно может сделать, это взглянуть на destinationViewController и инициировать вам универсальный, который имеет ограниченное использование.
К сожалению, потому что это полуобработанное решение, которое я придерживаюсь традиционного подхода на данный момент, поэтому все мои контроллеры представлений выделяются и init'd вручную, и что более важно, я добавил метод для каждого контроллера представления в initWithMOC:(MOC *)moc;
Мой архитектор говорит мне, что этот код является более надежным, я полагаю, что это вопрос мнения о том, стоит ли компромисс.
Кто-нибудь еще придумал лучший способ?
CA.
Не знаю, правильно ли я понял, но почему бы вам не оставить контекст управляемого объекта непосредственно в классе AppDelegate и оставить там всю логику для создания экземпляров. И с тех пор вы можете попросить об этом.
@property (readonly, strong, nonatomic) NSManagedObjectContext *managedObjectContext;
тогда вы можете вспомнить его в любое время из любого места.
NSManagedObjectContext *moc = [(YourApplicationDelegate*)[[UIApplication sharedApplication] delegate] managedObjectContext];
Для удобства я объявил определение для него:
#define MOC [(YourApplicationDelegate*)[[UIApplication sharedApplication] delegate] managedObjectContext]
Поэтому это становится:
[MOC save:&error];
Вы можете взять это где угодно. Просто попробуйте взглянуть на автоматически сгенерированный код для приложения CoreData в XCode, вы увидите, что многие средства доступа с CoreData находятся там, а само CoreData лениво инициализируется при первом запросе.