如何正确管理每个视图控制器中的 NSManagedObjectContext?
Posted
技术标签:
【中文标题】如何正确管理每个视图控制器中的 NSManagedObjectContext?【英文标题】:How to correctly manage the NSManagedObjectContext in each view controller? 【发布时间】:2013-10-01 18:15:02 【问题描述】:我对 CoreData 比较陌生,我想知道我做的是否正确。首先文档说:
“按照惯例,您从视图控制器获取上下文。不过,您必须适当地实现您的应用程序才能遵循此模式。
当你实现一个与 Core Data 集成的视图控制器时,你可以添加一个 NSManagedObjectContext 属性。
当你创建一个视图控制器时,你将它应该使用的上下文传递给它。您传递现有的上下文,或者(在您希望新控制器管理一组离散的编辑的情况下)为它创建的新上下文。应用程序委托通常负责创建上下文以传递给显示的第一个视图控制器。"https://developer.apple.com/library/ios/documentation/DataManagement/Conceptual/CoreDataSnippets/Articles/stack.html
所以我要做的是为我的 NSManagedObjectContext 创建一个属性:
MyViewController.H
@interface MyViewController : ViewController
NSManagedObjectContext *moc;
@property (nonatomic, retain) NSManagedObjectContext *moc;
@end
MyViewController.m
@implementation MyViewController
@synthesize moc=moc;
1.-我想对数据库进行一些更改的任何地方都这样做。
MainNexarAppDelegate *appDelegate =
[[UIApplication sharedApplication] delegate];
self.moc = [[NSManagedObjectContext alloc] init];
self.moc.persistentStoreCoordinator = [appDelegate persistentStoreCoordinator];
/*code**/
[self.moc save:&error];
2-.如果我要在不同的线程中工作,我有我的自定义方法来创建带有 NSPrivateQueueConcurrencyType 的 NSManagedObjectContext 以便可以在私有队列中进行管理:
//Myclass NSObject<br>
-(NSManagedObjectContext *)createManagedObjectContext
MainNexarAppDelegate *appDelegate =
[[UIApplication sharedApplication] delegate];
NSPersistentStoreCoordinator *coordinator = [appDelegate persistentStoreCoordinator];
if (coordinator != nil)
__managedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
[__managedObjectContext setPersistentStoreCoordinator:coordinator];
return __managedObjectContext;
//__managedObjectContext is my property from the .h file
//@property (readonly,strong,nonatomic) NSManagedObjectContext* managedObjectContext;
-
为每个视图控制器创建一个 NSManagedObjectContext 是一种好的做法,您将在其中对数据库进行一些更改?
1.1。使用 [UIApplication sharedApplication] 从 appdelegate 获取持久的 NSPersistentStoreCoordinator 是一种有效的方法吗?
在主线程和任何其他线程之间共享持久存储协调器是否安全?
任何帮助将不胜感激:)。
【问题讨论】:
【参考方案1】:我不同意这里的大多数答案。这对#1 来说还不错。事实上,在大多数情况下这样做可能是一种很好的做法。特别是如果你有不同的线程运行东西。它极大地简化了我的应用程序,以便在需要时创建 NSManagedObjectContexts,包括每个视图控制器。这也是 MagicalRecord 背后的人推荐的(这是我在大多数情况下使用 Core Data 的方法)。 NSManagedObjectContext 创建对于 MR 人员来说并不是一个高开销调用。无论如何,我都不是 CoreData 专家,但是按照 MagicalRecord 的人向我推荐的方式,我这样做的效果要好得多。
【讨论】:
嘿@chadbag!你能提供一些关于这种方法的代码sn-ps吗?我也使用 MagicalRecord,但似乎我没有以正确的方式管理上下文。 @Darmen 对不起,本离开。代码 sn-ps 究竟是为了什么?这是一个非常广泛的话题 不仅仅是线程。为一组离散的编辑创建子上下文,如编辑项视图控制器。然后控制器可以使用核心数据的力量进行 UI 更新,但不需要撤消对主上下文的更改,它可以丢弃其子上下文。【参考方案2】:我只能为问题 #1 提供帮助。以下是 Apple 文档在将上下文传递给您的视图控制器时的含义示例。在这种情况下,应用代理在应用代理创建上下文后将上下文传递给根视图控制器。
// in AppDelegate.m (using storyboard)
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
// Override point for customization after application launch.
// let's assume that your MyViewController is the root view controller; grab a reference to the root view controller
MyViewController *rootViewController = (MyViewController *)self.window.rootViewController;
// initialize the Core Data stack...
rootViewController.moc = ... // pass the context to your view controller
return YES;
【讨论】:
+1 按本书完成。我对任何从单个视图控制器引用 App Delegate 的代码 sn-p 都非常怀疑。【参考方案3】:这不是必需的,但它是一种强大的技术,可以在视图控制器中管理一组离散的编辑,该控制器将用于编辑项目,例如就像下面来自Apple's CoreDataBooks sample 的sn-p 一样。您想要这样做的原因是您可以使用 Core Data 的所有功能来更新您的 UI,而无需撤消主上下文中的所有内容,如果用户决定取消,则可以简单地丢弃子上下文。也可以通过NSManagedObjectContext
的新automaticallyMergesChangesFromParent
属性将父项更改下推给子项。
RootViewController.m
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
if ([[segue identifier] isEqualToString:@"AddBook"])
/*
The destination view controller for this segue is an AddViewController to manage addition of the book.
This block creates a new managed object context as a child of the root view controller's context. It then creates a new book using the child context. This means that changes made to the book remain discrete from the application's managed object context until the book's context is saved.
The root view controller sets itself as the delegate of the add controller so that it can be informed when the user has completed the add operation -- either saving or canceling (see addViewController:didFinishWithSave:).
IMPORTANT: It's not necessary to use a second context for this. You could just use the existing context, which would simplify some of the code -- you wouldn't need to perform two saves, for example. This implementation, though, illustrates a pattern that may sometimes be useful (where you want to maintain a separate set of edits).
*/
UINavigationController *navController = (UINavigationController *)[segue destinationViewController];
AddViewController *addViewController = (AddViewController *)[navController topViewController];
addViewController.delegate = self;
// Create a new managed object context for the new book; set its parent to the fetched results controller's context.
NSManagedObjectContext *addingContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];
[addingContext setParentContext:[self.fetchedResultsController managedObjectContext]];
Book *newBook = (Book *)[NSEntityDescription insertNewObjectForEntityForName:@"Book" inManagedObjectContext:addingContext];
addViewController.book = newBook;
addViewController.managedObjectContext = addingContext;
else if ([[segue identifier] isEqualToString:@"ShowSelectedBook"])
NSIndexPath *indexPath = [self.tableView indexPathForSelectedRow];
Book *selectedBook = (Book *)[[self fetchedResultsController] objectAtIndexPath:indexPath];
// Pass the selected book to the new view controller.
DetailViewController *detailViewController = (DetailViewController *)[segue destinationViewController];
detailViewController.book = selectedBook;
#pragma mark - Add controller delegate
/*
Add controller's delegate method; informs the delegate that the add operation has completed, and indicates whether the user saved the new book.
*/
- (void)addViewController:(AddViewController *)controller didFinishWithSave:(BOOL)save
if (save)
/*
The new book is associated with the add controller's managed object context.
This means that any edits that are made don't affect the application's main managed object context -- it's a way of keeping disjoint edits in a separate scratchpad. Saving changes to that context, though, only push changes to the fetched results controller's context. To save the changes to the persistent store, you have to save the fetch results controller's context as well.
*/
NSError *error;
NSManagedObjectContext *addingManagedObjectContext = [controller managedObjectContext];
if (![addingManagedObjectContext 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();
if (![[self.fetchedResultsController managedObjectContext] 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();
// Dismiss the modal view to return to the main list
[self dismissViewControllerAnimated:YES completion:nil];
【讨论】:
【参考方案4】:-
不,为每个人创建
NSManagedObjectContext
不好
控制器。您所需要的只是为每个线程拥有自己的上下文。所以
这取决于你的逻辑。
1.1。是的,还不错。
是的,它是安全的。
在我的应用程序中,我使用共享 NSPersistentStoreCoordinator
的单例类。如果我需要创建新的上下文,我会使用
self.context = [NSManagedObjectContext new];
self.context.persistentStoreCoordinator = [[SharedStorage sharedStorage] storeCoordinator];
有点详细的代码sn -p here。通常我使用NSManagedObjectContext
的视图控制器有表视图,所以我使用NSFetchedResultsController
。而且我对所有这些控制器只使用一个共享上下文。
注意 1: 有人说单例是个坏主意。
注意 2: 不要忘记您需要通过 save
和 merge
方法同步所有上下文。
【讨论】:
【参考方案5】:不好:在数据库中为每个要进行一些更改的 viewController 创建 NSManagedObjectContexts 好:创建一个单一的 NSManagedObjectContext aka singleton,它将被传递给那些 想要在数据库中进行一些更改的视图控制器, 这意味着,尽管您可以在一个应用程序中拥有多个数据库,但该应用程序基本上只有一个数据库。 示例 1: 假设您创建了一个基于 Tab 的应用程序,那么窗口的 rootViewController 将是 UITabBarController,您可以从根目录获取所有其他控制器!在这里你可以将上下文传递给他们
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
/*
MUNSharedDatabaseController is the singleton
*/
UITabBarController *tabBarController = (UITabBarController *)self.window.rootViewController;
UIViewController *firstViewController = tabBarController.viewControllers[0];
firstViewController.managedObjectContext = [[MUNSharedDatabaseController sharedDatabaseController] managedObjectContext];
UIViewController *secondViewController = tabBarController.viewControllers[1];
secondVC.managedObjectContext = [[MUNSharedDatabaseController sharedDatabaseController] managedObjectContext];
// otherStuff, or etc.
还有很棒的核心数据库,又名 MagicalRecord 你可以在这里查看:https://github.com/magicalpanda/MagicalRecord 如果你想节省时间,它真的很棒,但它不能替代 Core Data 。 这里还有一个如何创建核心数据单例的示例:http://steamheadstudio.pl/wordpress/core-data-singleton-with-sample-code/
【讨论】:
MagicalRecord 为几乎所有内容创建了一个新的上下文(有点夸张)。根据 MagicalRecord 背后的人的说法,创建的 NSManagedObjectContexts 是一种低开销调用,并且在需要时创建一个并没有什么坏处。根据应用程序逻辑,为每个视图控制器创建一个也不错。在大多数情况下,我使用的是 MagicalRecord 而不是直接使用 CoreData,但它极大地简化了事情,并且可以在需要时更好地创建一个,包括每个视图控制器。 如果 MagicalRecord 能提供您想要的,那就太好了!但是对于那些想知道事情是如何运作的人来说,了解如何管理上下文非常重要,顺便说一句,默认的核心数据应用程序Xcode将NSManagedObjectContext
的引用传递给viewController,
但是没有什么说你必须传递一个上下文。这只是苹果给出的一个例子。即使我直接使用 Core Data,如果可以的话,我也会以类似的方式管理它,根据需要使用根上下文或父上下文以及本地上下文。是的,您还应该了解 Core Data 如何充分利用 MagicalRecord。
我知道回复有点晚了,但我也有一个单身人士并分享周围的环境。我看到很多帖子有很多建议。不知道我是不是真的做对了。以上是关于如何正确管理每个视图控制器中的 NSManagedObjectContext?的主要内容,如果未能解决你的问题,请参考以下文章
如何将@NSManaged 对象类中的变量初始化为 JSON 值?
如果它是子视图控制器,如何将 AppDelegate 中的 managedObjectContext 传递给正确的视图控制器?