如何在每个视图控制器中正确管理NSManagedObjectContext?

时间:2013-10-01 18:15:02

标签: ios objective-c core-data

我与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;
  1. 一个好的做法是为每个视图控制器创建一个NSManagedObjectContext,在那里你将对数据库进行一些更改?
    1.1。它是一种有效的方法,使用[UIApplication sharedApplication]来获得持久的NSPersistentStoreCoordinator形成appdelegate?
  2. 在主线程和任何其他线程之间共享持久存储协调器是否安全?
  3. 任何帮助将不胜感激:)。

5 个答案:

答案 0 :(得分:3)

我不同意这里的大部分答案。它对#1来说并不坏。事实上,在大多数情况下这样做可能是很好的做法。特别是如果你有不同的线程运行的东西。它大大简化了我的应用程序,以便在需要时创建NSManagedObjectContexts,包括每个视图控制器。这也是MagicalRecord背后的人推荐的(在大多数情况下,我使用它来使用Core Data)。每个MR人员创建NSManagedObjectContext不是一个高开销。我不是任何想象中的CoreData专家,但是我这样做的效果要好得多,正如MagicalRecord人员所推荐的那样。

答案 1 :(得分:1)

我只能为问题#1提供帮助。以下是Apple文档在将上下文传递给视图控制器时的含义示例。在这种情况下,app委托在应用委托创建上下文后将上下文传递给根视图控制器。

// 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;
}

答案 2 :(得分:0)

这不是必需的,但它是一种强大的技术,可以在视图控制器中管理离散的一组编辑内容,这些控件将用于编辑项目,例如就像下面Apple's CoreDataBooks sample中的摘录一样。之所以这样做,是因为您可以使用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];
}

答案 3 :(得分:-1)

  1. 不,为每个人创建NSManagedObjectContext并不好 控制器。您所需要的只是为每个线程拥有自己的上下文。所以 这取决于你的逻辑。 1.1。是的,这还不错。
  2. 是的,这是安全的。
  3. 在我的应用中,我使用带有共享NSPersistentStoreCoordinator的单例类。如果我需要创建新的上下文,我使用

    self.context = [NSManagedObjectContext new];
    self.context.persistentStoreCoordinator = [[SharedStorage sharedStorage] storeCoordinator];
    

    一些详细的代码段here。通常我使用NSManagedObjectContext的视图控制器具有表视图,因此我使用NSFetchedResultsController。我只为所有这些控制器使用一个共享上下文。

    注1:有人说拥有单身人士是个坏主意。

    注2:不要忘记您需要通过savemerge方法同步所有上下文。

答案 4 :(得分:-1)

错误:为数据库中要进行一些更改的每个viewController创建NSManagedObjectContexts 好:创建一个NSManagedObjectContext aka singleton,将传递给那些 查看想要在数据库中进行一些更改的控制器, 这意味着,因为app基本上只有一个数据库,尽管你可以在一个应用程序中拥有多个数据库。 例1: 假设您创建了一个基于Tab的应用程序,那么窗口的rootViewController将是UITabBarController,从root可以获得所有其他控制器!在这里你可以传递它们的上下文

- (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/