Core Data: UIManagedDocument or AppDelegate to set

2020-02-24 07:37发布

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?

2条回答
▲ chillily
2楼-- · 2020-02-24 07:50

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.
查看更多
3楼-- · 2020-02-24 08:04

The singleton method above is very slick. A much simpler approach is to treat the appDelegate as a singleton, which it is. Create the properties in your appDelegate then a version of the following line will retrieve the one you want. For example to retrieve the moc

self.managedObjectContext = [(MyAppDelegate *)[[UIApplication sharedApplication] delegate] managedObjectContext];
查看更多
登录 后发表回答