Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Core Data: UIManagedDocument or AppDelegate to setup core data stack?

I am a little confused about where to setup my Core data Stack. In the Appdelegate or using a shared instance of UIManagedDocument, described here?: http://adevelopingstory.com/blog/2012/03/core-data-with-a-single-shared-uimanageddocument.html

Right now I have by core data stack setup in my AppDelegate and I am passing my MOC around between the viewcontrollers. But would it be better to migrate to UIManagedDocument and make a shared Instance, so I don't have to pass the MOC around all the time? And also because its newer?

like image 963
7c9d6b001a87e497d6b96fbd4c6fdf Avatar asked Feb 14 '13 14:02

7c9d6b001a87e497d6b96fbd4c6fdf


People also ask

Should I use Core Data?

The next time you need to store data, you should have a better idea of your options. Core Data is unnecessary for random pieces of unrelated data, but it's a perfect fit for a large, relational data set. The defaults system is ideal for small, random pieces of unrelated data, such as settings or the user's preferences.

What is a Core Data stack?

After you create a data model file as described in Creating a Core Data Model, set up the classes that collaboratively support your app's model layer. These classes are referred to collectively as the Core Data stack.

What is Persistentcontainer in Core Data?

NSPersistentContainer simplifies the creation and management of the Core Data stack by handling the creation of the managed object model ( NSManagedObjectModel ), persistent store coordinator ( NSPersistentStoreCoordinator ), and the managed object context ( NSManagedObjectContext ).

How does Core Data handle multithreading?

To use Core Data in a multithreaded environment, ensure that: Managed object contexts are bound to the thread (queue) that they are associated with upon initialization. Managed objects retrieved from a context are bound to the same queue that the context is bound to.


1 Answers

UIManagedDocument is used to sync data (typically files) to iCloud. It is only tangentially related to Core Data.

Core Data setup is typically done in the AppDelegate so there is nothing wrong with what you are doing there. In fact, if you create a new project using Core Data that is how the Xcode template will do it.

You usually do not need to pass your ManagedObjectContext around from viewcontroller to viewcontroller. It is better to create a singleton Data Access layer which can provide context anywhere in your app. There are some cases where you might want to have a private MOC for a viewcontroller, but not very often.

Here's some code to create a singleton DataAccessLayer:

DataAccessLayer.h

@interface DataAccessLayer : NSObject

   //Saves the Data Model onto the DB
   - (void)saveContext;

   //DataAccessLayer singleton instance shared across application
   + (id) sharedInstance;
   + (void)disposeInstance;
   // Returns the managed object context for the application.
   // If the context doesn't already exist, it is created and bound 
   // to the persistent store coordinator for the application.
   + (NSManagedObjectContext *)context;
@end

DataAccessLayer.m

#import "DataAccessLayer.h"

//static instance for singleton implementation
static DataAccessLayer __strong *manager = nil;

//Private instance methods/properties
@interface DataAccessLayer ()

// Returns the managed object context for the application.
// If the context doesn't already exist, it is created and 
// bound to the persistent store coordinator for the application.
@property (readonly, strong, nonatomic) NSManagedObjectContext *managedObjectContext;

// Returns the managed object model for the application.
// If the model doesn't already exist, it is created from the application's model.
@property (readonly, strong, nonatomic) NSManagedObjectModel *managedObjectModel;

// Returns the persistent store coordinator for the application.
// If the coordinator doesn't already exist, it is created and the application's 
// store added to it.
@property (readonly,strong,nonatomic) NSPersistentStoreCoordinator *persistentStoreCoordinator;

// Returns the URL to the application's Documents directory.
- (NSURL *)applicationDocumentsDirectory;
@end


@implementation DataAccessLayer

@synthesize managedObjectContext = __managedObjectContext;
@synthesize managedObjectModel = __managedObjectModel;
@synthesize persistentStoreCoordinator = __persistentStoreCoordinator;

//DataAccessLayer singleton instance shared across application
+ (id)sharedInstance
{
    @synchronized(self) 
    {
        if (manager == nil)
            manager = [[self alloc] init];
    }
    return manager;
}

+ (void)disposeInstance
{
    @synchronized(self)
    {
        manager = nil;
    }
}

+(NSManagedObjectContext *)context
{
    return [[DataAccessLayer sharedInstance] managedObjectContext];
}

//Saves the Data Model onto the DB
- (void)saveContext
{
    NSError *error = nil;
    NSManagedObjectContext *managedObjectContext = self.managedObjectContext;
    if (managedObjectContext != nil) 
    {
        if ([managedObjectContext hasChanges] && ![managedObjectContext save:&error]) 
        {
            //Need to come up with a better error management here.
            NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
            abort();
        } 
    }
}

// Returns the managed object context for the application.
// If the context doesn't already exist, it is created and 
// bound to the persistent store coordinator for the application.
- (NSManagedObjectContext *)managedObjectContext
{
    if (__managedObjectContext != nil)
        return __managedObjectContext;

    NSPersistentStoreCoordinator *coordinator = [self persistentStoreCoordinator];
    if (coordinator != nil) 
    {
        __managedObjectContext = [[NSManagedObjectContext alloc] init];
        [__managedObjectContext setPersistentStoreCoordinator:coordinator];
    }
    return __managedObjectContext;
}

// Returns the managed object model for the application.
// If the model doesn't already exist, it is created from the 
// application's model.
- (NSManagedObjectModel *)managedObjectModel
{
    if (__managedObjectModel != nil) 
        return __managedObjectModel;

    NSURL *modelURL = [[NSBundle mainBundle] URLForResource:@"Model" 
                                               withExtension:@"momd"];
    __managedObjectModel = [[NSManagedObjectModel alloc] 
                         initWithContentsOfURL:modelURL];
    return __managedObjectModel;
}

// Returns the persistent store coordinator for the application.
// If the coordinator doesn't already exist, it is created and the 
// application's store added to it.
- (NSPersistentStoreCoordinator *)persistentStoreCoordinator
{
    if (__persistentStoreCoordinator != nil) 
        return __persistentStoreCoordinator;

    NSURL *storeURL = [[self applicationDocumentsDirectory] 
                         URLByAppendingPathComponent:@"MyData.sqlite"];

    NSError *error = nil;
    __persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc]
                                     initWithManagedObjectModel:[self managedObjectModel]];

    if (![__persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType
                           configuration:nil URL:storeURL options:nil error:&error]) 
    {
        NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
        abort();
    }    

    return __persistentStoreCoordinator;
}

// Returns the URL to the application's Documents directory.
- (NSURL *)applicationDocumentsDirectory
{
    return [[[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory
                                         inDomains:NSUserDomainMask] lastObject];
}

@end

Any time you need context, you can grab it using:

NSManagedObjectContext *context = [DataAccessLayer context];

This design generally works well. NSManagedObjectContext is a very lightweight object, so there isn't a real performance penalty keeping it around. However, if you need to do Core Data stuff on other threads, the design needs to change a bit. From Apple's docs:

You must create the managed context on the thread on which it will be used. If you use NSOperation, note that its init method is invoked on the same thread as the caller. You must not, therefore, create a managed object context for the queue in the queue’s init method, otherwise it is associated with the caller’s thread. Instead, you should create the context in main (for a serial queue) or start (for a concurrent queue).

Using thread confinement, you should not pass managed objects or managed object contexts between threads. To “pass” a managed object from one context another across thread boundaries, you either:

  • Pass its object ID (objectID) and use objectWithID: or existingObjectWithID:error: on the receiving managed object context. The corresponding managed objects must have been saved—you cannot pass the ID of a newly-inserted managed object to another context.
  • Execute a fetch on the receiving context.
like image 148
memmons Avatar answered Sep 30 '22 10:09

memmons