Objective-C中从AppDelegate传递NSPersistentContainer至根视图控制器问题
Core Data栈Objective-C实现中视图控制器无法获取PersistentContainer的问题
问题背景
我按照《Setting Up a Core Data Stack》文档搭建Objective-C版本的Core Data栈,但文档仅提供Swift实现。应用结构为:Application Entry Point->UINavigationController->TodoListTableViewController。
在-[UIApplicationDelegate application:didFinishLaunchingWithOptions:]中编写了初始化并传递PersistentContainer的代码,但在TodoListTableViewController的viewDidLoad中检查时触发了“missing container”异常。
相关代码片段
AppDelegate的启动方法代码
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { // Override point for customization after application launch. if (self.persistentContainer == nil) { [NSException raise:@"did not initialize persistent container in app delegate" format:@"no init"]; } UIStoryboard *mainStoryboard = [UIStoryboard storyboardWithName:@"Main" bundle:nil]; TodoListTableViewController *todoListVC = [mainStoryboard instantiateViewControllerWithIdentifier:@"TodoListTableViewController"]; todoListVC.persistentContainer = self.persistentContainer; UINavigationController *navController = [[UINavigationController alloc] initWithRootViewController:todoListVC]; self.window.rootViewController = navController; return YES; }
TodoListTableViewController的viewDidLoad代码
- (void)viewDidLoad { [super viewDidLoad]; if (self.persistentContainer == nil) { [NSException raise:@"missing container" format:@"missing container"]; } }
AppDelegate中Xcode生成的Core Data模板代码
@synthesize persistentContainer = _persistentContainer; - (NSPersistentContainer *)persistentContainer { // The persistent container for the application. This implementation creates and returns a container, having loaded the store for the application to it. @synchronized (self) { if (_persistentContainer == nil) { _persistentContainer = [[NSPersistentContainer alloc] initWithName:@"ToDoList"]; [_persistentContainer loadPersistentStoresWithCompletionHandler:^(NSPersistentStoreDescription *storeDescription, NSError *error) { if (error != nil) { // Replace this implementation with code to handle the error appropriately. // abort() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development. /* Typical reasons for an error here include: * The parent directory does not exist, cannot be created, or disallows writing. * The persistent store is not accessible, due to permissions or data protection when the device is locked. * The device is out of space. * The store could not be migrated to the current model version. Check the error message to determine what the actual problem was. */ NSLog(@"Unresolved error %@, %@", error, error.userInfo); abort(); } }]; } } return _persistentContainer; } #pragma mark - Core Data Saving support - (void)saveContext { NSManagedObjectContext *context = self.persistentContainer.viewContext; NSError *error = nil; if ([context hasChanges] && ![context save:&error]) { // Replace this implementation with code to handle the error appropriately. // abort() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development. NSLog(@"Unresolved error %@, %@", error, error.userInfo); abort(); } }
AppDelegate头文件代码
@property (readonly, strong) NSPersistentContainer *persistentContainer;
问题原因与解决方案
核心原因
你手动创建了新的TodoListTableViewController实例并传递PersistentContainer,但Storyboard中已经初始化了导航控制器及其根视图控制器(即实际显示的TodoListTableViewController),两者不是同一个对象,导致真正显示的控制器没有拿到PersistentContainer。
修复步骤
- 修改AppDelegate的启动代码,直接获取Storyboard中已有的导航控制器和对应的根视图控制器:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { if (self.persistentContainer == nil) { [NSException raise:@"did not initialize persistent container in app delegate" format:@"no init"]; } UIStoryboard *mainStoryboard = [UIStoryboard storyboardWithName:@"Main" bundle:nil]; // 获取Storyboard中设置的初始导航控制器 UINavigationController *navController = [mainStoryboard instantiateInitialViewController]; // 从导航控制器中取出实际显示的根视图控制器 TodoListTableViewController *todoListVC = (TodoListTableViewController *)navController.topViewController; todoListVC.persistentContainer = self.persistentContainer; self.window.rootViewController = navController; return YES; }
- 确保TodoListTableViewController正确声明属性,在
TodoListTableViewController.h中添加:
@property (strong, nonatomic) NSPersistentContainer *persistentContainer;
- 验证Storyboard配置:
- 确认
UINavigationController已勾选Is Initial View Controller - 确认
TodoListTableViewController是导航控制器的根视图控制器
额外说明
loadPersistentStoresWithCompletionHandler:是异步执行,但persistentContainer的getter方法中通过@synchronized保证了初始化的同步性,因此didFinishLaunching中访问时容器已经初始化完成,不存在异步导致的空值问题。
内容的提问来源于stack exchange,提问作者reactor
相关产品推荐
相关产品推荐

