core data with multiple managed object contexts

54
Core Data with multiple managed object contexts Photo by Free-Photo-Gallery.org

Upload: matthew-morey

Post on 12-May-2015

20.533 views

Category:

Technology


0 download

DESCRIPTION

When using Core Data for persisting app data multiple managed object contexts (MOC) are often required to avoid blocking UI. Typically you would create a background MOC and listen for changes on the main MOC, merging changes as necessary. With iOS 5, MOCs now have parent context and the ability to set concurrency types. These new features greatly simplify dealing with Core Data on background queues. During this presentation Matt will cover the pros and cons of this new method of dealing with Core Data.

TRANSCRIPT

Page 1: Core Data with multiple managed object contexts

Core Datawith multiple managed

object contexts

Photo by Free-Photo-Gallery.org

Page 2: Core Data with multiple managed object contexts

Core Datawith multiple managed

object contexts

Photo by Free-Photo-Gallery.org

Page 3: Core Data with multiple managed object contexts

Matt(hew) Morey

matthewmorey.com | @xzolian

Senior Developer at ChaiONETraveler

Boardsport Junkie

Page 4: Core Data with multiple managed object contexts

Agenda

1)Core Data Basics2)Concurrency Problems3)Concurrency Solutions

Page 5: Core Data with multiple managed object contexts

Agenda

1)Core Data Basics2)Concurrency Problems3)Concurrency Solutions

Page 6: Core Data with multiple managed object contexts

Basics

Page 7: Core Data with multiple managed object contexts

Basics

AppAppAppAppAppApp

NSManagedObjectNSManagedObjectNSManagedObjectNSManagedObjectNSManagedObjectNSManagedObject

NSManagedObjectContextNSManagedObjectContextNSManagedObjectContextNSManagedObjectContextNSManagedObjectContextNSManagedObjectContext

NSPersistentStoreCoordinatorNSPersistentStoreCoordinatorNSPersistentStoreCoordinator NSManagedObjectModelNSManagedObjectModelNSManagedObjectModel

NSPersistentStoreNSPersistentStoreNSPersistentStoreNSPersistentStoreNSPersistentStoreNSPersistentStore

SQLite XML BinaryBinary In Memory Custom

Page 8: Core Data with multiple managed object contexts

Basics

AppAppAppAppAppApp

NSManagedObjectNSManagedObjectNSManagedObjectNSManagedObjectNSManagedObjectNSManagedObject

NSManagedObjectContextNSManagedObjectContextNSManagedObjectContextNSManagedObjectContextNSManagedObjectContextNSManagedObjectContext

NSPersistentStoreCoordinatorNSPersistentStoreCoordinatorNSPersistentStoreCoordinator NSManagedObjectModelNSManagedObjectModelNSManagedObjectModel

NSPersistentStoreNSPersistentStoreNSPersistentStoreNSPersistentStoreNSPersistentStoreNSPersistentStore

SQLite XML BinaryBinary In Memory Custom

Page 9: Core Data with multiple managed object contexts

Managed Object Model

Page 10: Core Data with multiple managed object contexts

Managed Object Model

Page 11: Core Data with multiple managed object contexts

Managed Object Model- (NSManagedObjectModel *)managedObjectModel{ if (_managedObjectModel != nil) { return _managedObjectModel; } NSURL *modelURL = [[NSBundle mainBundle] URLForResource:@"core-data" withExtension:@"momd"]; _managedObjectModel = [[NSManagedObjectModel alloc] initWithContentsOfURL:modelURL]; return _managedObjectModel;}

Page 12: Core Data with multiple managed object contexts

Persistent Store Coordinator

Page 13: Core Data with multiple managed object contexts

Persistent STore Coordinator- (NSPersistentStoreCoordinator *)persistentStoreCoordinator{ if (_persistentStoreCoordinator != nil) { return _persistentStoreCoordinator; } NSURL *storeURL = [[self applicationDocumentsDirectory] URLByAppendingPathComponent:@"core-data.sqlite"]; NSError *error = nil; _persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:[self managedObjectModel]]; if (![_persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeURL options:nil error:&error]) { /* Replace this implementation with code to handle the error appropriately. ... */ NSLog(@"Unresolved error %@, %@", error, [error userInfo]); abort(); } return _persistentStoreCoordinator;}

Page 14: Core Data with multiple managed object contexts

Persistent STore Coordinator- (NSPersistentStoreCoordinator *)persistentStoreCoordinator{ if (_persistentStoreCoordinator != nil) { return _persistentStoreCoordinator; } NSURL *storeURL = [[self applicationDocumentsDirectory] URLByAppendingPathComponent:@"core-data.sqlite"]; NSError *error = nil; _persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:[self managedObjectModel]]; if (![_persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeURL options:nil error:&error]) { /* Replace this implementation with code to handle the error appropriately. ... */ NSLog(@"Unresolved error %@, %@", error, [error userInfo]); abort(); } return _persistentStoreCoordinator;}

Page 15: Core Data with multiple managed object contexts

Managed Object Context

Page 16: Core Data with multiple managed object contexts

Managed Object Context- (NSManagedObjectContext *)managedObjectContext{ if (_managedObjectContext != nil) { return _managedObjectContext; } NSPersistentStoreCoordinator *coordinator = [self persistentStoreCoordinator];

if (coordinator != nil) { _managedObjectContext = [[NSManagedObjectContext alloc] init]; [_managedObjectContext setPersistentStoreCoordinator:coordinator]; } return _managedObjectContext;}

Page 17: Core Data with multiple managed object contexts

Single Context

Page 18: Core Data with multiple managed object contexts

Single Context

Page 19: Core Data with multiple managed object contexts

Single Context

Code Examplehttps://github.com/mmorey/CoreDataMultiContext/tree/blocking

Page 20: Core Data with multiple managed object contexts

Agenda

1)Core Data Basics2)Concurrency Problems3)Concurrency Solutions

Page 21: Core Data with multiple managed object contexts

Problems

‣Core Data Managed Objects are not thread safe‣Must pass Object IDs to use across threads

‣Objects are locked for all operations including read‣Objects that feed the UI must be fetched on the main thread

Page 22: Core Data with multiple managed object contexts

Agenda

1)Core Data Basics2)Concurrency Problems3)Concurrency Solutions

Page 23: Core Data with multiple managed object contexts

Traditional Multi-Context

Pre-iOS 5: Thread Confinement‣Single NSMangedObjectContext per thread‣Manual notifications, merging, and saving‣Fairly easy to understand, but harder to manage

Page 24: Core Data with multiple managed object contexts

Traditional Multi-Context

Page 25: Core Data with multiple managed object contexts

Traditional Multi-Context- (void)loadData{ dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{

// Create temp context NSManagedObjectContext *context = [[NSManagedObjectContext alloc] init]; [context setPersistentStoreCoordinator:[(AppDelegate *)[[UIApplication sharedApplication] delegate] persistentStoreCoordinator]]; // // Do lots of async work here // // Save the context. error = nil; if (![context save:&error]) { // Replace this implementation with code to handle the error appropriately. abort(); } });}

// Register for save notification in ViewDidLoad[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(contextDidSave:) name:NSManagedObjectContextDidSaveNotification object:context];

Page 26: Core Data with multiple managed object contexts

Traditional Multi-Context- (void)loadData{ dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{

// Create temp context NSManagedObjectContext *context = [[NSManagedObjectContext alloc] init]; [context setPersistentStoreCoordinator:[(AppDelegate *)[[UIApplication sharedApplication] delegate] persistentStoreCoordinator]]; // // Do lots of async work here // // Save the context. error = nil; if (![context save:&error]) { // Replace this implementation with code to handle the error appropriately. abort(); } });}

// Register for save notification in ViewDidLoad[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(contextDidSave:) name:NSManagedObjectContextDidSaveNotification object:context];

Page 27: Core Data with multiple managed object contexts

Traditional Multi-Context- (void)loadData{ dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{

// Create temp context NSManagedObjectContext *context = [[NSManagedObjectContext alloc] init]; [context setPersistentStoreCoordinator:[(AppDelegate *)[[UIApplication sharedApplication] delegate] persistentStoreCoordinator]]; // // Do lots of async work here // // Save the context. error = nil; if (![context save:&error]) { // Replace this implementation with code to handle the error appropriately. abort(); } });}

// Register for save notification in ViewDidLoad[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(contextDidSave:) name:NSManagedObjectContextDidSaveNotification object:context];

Page 28: Core Data with multiple managed object contexts

Traditional Multi-Context- (void)contextDidSave:(NSNotification *)notification { dispatch_async(dispatch_get_main_queue(), ^{ NSManagedObjectContext *mainContext = [self.fetchedResultsController managedObjectContext]; [mainContext mergeChangesFromContextDidSaveNotification:notification]; });}

// Or

- (void)contextDidSave:(NSNotification *)notification { [self.managedObjectContext performSelectorOnMainThread: @selector(mergeChangesFromContextDidSaveNotification:) withObject:notification waitUntilDone:NO];}

Page 29: Core Data with multiple managed object contexts

Traditional Multi-Context- (void)contextDidSave:(NSNotification *)notification { dispatch_async(dispatch_get_main_queue(), ^{ NSManagedObjectContext *mainContext = [self.fetchedResultsController managedObjectContext]; [mainContext mergeChangesFromContextDidSaveNotification:notification]; });}

// Or

- (void)contextDidSave:(NSNotification *)notification { [self.managedObjectContext performSelectorOnMainThread: @selector(mergeChangesFromContextDidSaveNotification:) withObject:notification waitUntilDone:NO];}

Page 30: Core Data with multiple managed object contexts

Traditional Multi-Context

Code Examplehttps://github.com/mmorey/CoreDataMultiContext/tree/notification-context

Page 31: Core Data with multiple managed object contexts

Parent Child Context

≥ iOS 5: Parent Child Contexts‣Grand Central Dispatch private dispatch queues‣Threading managed for you, no manual synchronization required‣Less complicated and more flexible than pre-iOS 5 method ‣Context can and should be nested

Page 32: Core Data with multiple managed object contexts

Concurrency Types

‣NSConfinementConcurrencyType‣Separate contexts for each thread‣Default, Legacy option

‣NSPrivateQueueConcurrencyType‣MOC maintains private serialized queue‣Can be created from any other thread‣Idle queues are more efficient than extra threads

‣NSMainQueueConcurrencyType‣Similar to private queue but on the main queue

NSManagedObjectContext *temporaryContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];

Page 33: Core Data with multiple managed object contexts

Parent Child Context

Page 34: Core Data with multiple managed object contexts

Parent Child Context

PSC PSC

Page 35: Core Data with multiple managed object contexts

Parent Child Context__block NSManagedObjectContext *managedObjectContext = [(AppDelegate *)[[UIApplication sharedApplication] delegate] managedObjectContext];NSManagedObjectContext *temporaryContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];temporaryContext.parentContext = managedObjectContext; [temporaryContext performBlock:^{

// // Do lots of async work here // // Save the context. NSError *error = nil; if (![temporaryContext save:&error]) {abort();} [managedObjectContext performBlock:^{ // Save the context. NSError *error = nil; if (![managedObjectContext save:&error]) {abort();}

}]; // main}]; // temp context

Page 36: Core Data with multiple managed object contexts

Parent Child Context__block NSManagedObjectContext *managedObjectContext = [(AppDelegate *)[[UIApplication sharedApplication] delegate] managedObjectContext];NSManagedObjectContext *temporaryContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];temporaryContext.parentContext = managedObjectContext; [temporaryContext performBlock:^{

// // Do lots of async work here // // Save the context. NSError *error = nil; if (![temporaryContext save:&error]) {abort();} [managedObjectContext performBlock:^{ // Save the context. NSError *error = nil; if (![managedObjectContext save:&error]) {abort();}

}]; // main}]; // temp context

Page 37: Core Data with multiple managed object contexts

Parent Child Context__block NSManagedObjectContext *managedObjectContext = [(AppDelegate *)[[UIApplication sharedApplication] delegate] managedObjectContext];NSManagedObjectContext *temporaryContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];temporaryContext.parentContext = managedObjectContext; [temporaryContext performBlock:^{

// // Do lots of async work here // // Save the context. NSError *error = nil; if (![temporaryContext save:&error]) {abort();} [managedObjectContext performBlock:^{ // Save the context. NSError *error = nil; if (![managedObjectContext save:&error]) {abort();}

}]; // main}]; // temp context

Private Queue save propagates up to parent

Page 38: Core Data with multiple managed object contexts

Parent Child Context__block NSManagedObjectContext *managedObjectContext = [(AppDelegate *)[[UIApplication sharedApplication] delegate] managedObjectContext];NSManagedObjectContext *temporaryContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];temporaryContext.parentContext = managedObjectContext; [temporaryContext performBlock:^{

// // Do lots of async work here // // Save the context. NSError *error = nil; if (![temporaryContext save:&error]) {abort();} [managedObjectContext performBlock:^{ // Save the context. NSError *error = nil; if (![managedObjectContext save:&error]) {abort();}

}]; // main}]; // temp context

Save to disc still locks PS which will block the UI during

read operations

Page 39: Core Data with multiple managed object contexts

Basics

AppAppAppAppAppApp

NSManagedObjectNSManagedObjectNSManagedObjectNSManagedObjectNSManagedObjectNSManagedObject

NSManagedObjectContextNSManagedObjectContextNSManagedObjectContextNSManagedObjectContextNSManagedObjectContextNSManagedObjectContext

NSPersistentStoreCoordinatorNSPersistentStoreCoordinatorNSPersistentStoreCoordinator NSManagedObjectModelNSManagedObjectModelNSManagedObjectModel

NSPersistentStoreNSPersistentStoreNSPersistentStoreNSPersistentStoreNSPersistentStoreNSPersistentStore

SQLite XML BinaryBinary In Memory Custom

Page 40: Core Data with multiple managed object contexts

Async Saving - Parent Child Context

Page 41: Core Data with multiple managed object contexts

Async Saving - Parent Child Context// Child context- (NSManagedObjectContext *)managedObjectContext{ if (_managedObjectContext != nil) { return _managedObjectContext; } _managedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType]; _managedObjectContext.parentContext = [self writerManagedObjectContext]; return _managedObjectContext;}

// Parent context- (NSManagedObjectContext *)writerManagedObjectContext{ if (_writerManagedObjectContext != nil) { return _writerManagedObjectContext; } NSPersistentStoreCoordinator *coordinator = [self persistentStoreCoordinator]; if (coordinator != nil) { _writerManagedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType: NSPrivateQueueConcurrencyType]; [_writerManagedObjectContext setPersistentStoreCoordinator:coordinator]; } return _writerManagedObjectContext;}

Page 42: Core Data with multiple managed object contexts

Async Saving - Parent Child Context// Child context- (NSManagedObjectContext *)managedObjectContext{ if (_managedObjectContext != nil) { return _managedObjectContext; } _managedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType]; _managedObjectContext.parentContext = [self writerManagedObjectContext]; return _managedObjectContext;}

// Parent context- (NSManagedObjectContext *)writerManagedObjectContext{ if (_writerManagedObjectContext != nil) { return _writerManagedObjectContext; } NSPersistentStoreCoordinator *coordinator = [self persistentStoreCoordinator]; if (coordinator != nil) { _writerManagedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType: NSPrivateQueueConcurrencyType]; [_writerManagedObjectContext setPersistentStoreCoordinator:coordinator]; } return _writerManagedObjectContext;}

Page 43: Core Data with multiple managed object contexts

Async Saving - Parent Child Context// Child context- (NSManagedObjectContext *)managedObjectContext{ if (_managedObjectContext != nil) { return _managedObjectContext; } _managedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType]; _managedObjectContext.parentContext = [self writerManagedObjectContext]; return _managedObjectContext;}

// Parent context- (NSManagedObjectContext *)writerManagedObjectContext{ if (_writerManagedObjectContext != nil) { return _writerManagedObjectContext; } NSPersistentStoreCoordinator *coordinator = [self persistentStoreCoordinator]; if (coordinator != nil) { _writerManagedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType: NSPrivateQueueConcurrencyType]; [_writerManagedObjectContext setPersistentStoreCoordinator:coordinator]; } return _writerManagedObjectContext;}

Page 44: Core Data with multiple managed object contexts

Parent Child Context__block NSManagedObjectContext *writerObjectContext = [(AppDelegate *)[[UIApplication sharedApplication] delegate] writerManagedObjectContext];__block NSManagedObjectContext *managedObjectContext = [(AppDelegate *)[[UIApplication sharedApplication] delegate] managedObjectContext];__block NSManagedObjectContext *temporaryContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];temporaryContext.parentContext = managedObjectContext; [temporaryContext performBlock:^{ // // Do lots of async work here // [temporaryContext save:&error]; {abort();} // Save the context. [managedObjectContext performBlock:^{ [managedObjectContext save:&error]; {abort();} // Save the context.

[writerObjectContext performBlock:^{ [writerObjectContext save:&error]; {abort();} // Save the context. }]; // writer }]; // main}]; // temp context

Page 45: Core Data with multiple managed object contexts

Parent Child Context__block NSManagedObjectContext *writerObjectContext = [(AppDelegate *)[[UIApplication sharedApplication] delegate] writerManagedObjectContext];__block NSManagedObjectContext *managedObjectContext = [(AppDelegate *)[[UIApplication sharedApplication] delegate] managedObjectContext];__block NSManagedObjectContext *temporaryContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];temporaryContext.parentContext = managedObjectContext; [temporaryContext performBlock:^{ // // Do lots of async work here // [temporaryContext save:&error]; {abort();} // Save the context. [managedObjectContext performBlock:^{ [managedObjectContext save:&error]; {abort();} // Save the context.

[writerObjectContext performBlock:^{ [writerObjectContext save:&error]; {abort();} // Save the context. }]; // writer }]; // main}]; // temp context

NSMainQueueConcurrency

NSPrivateQueueConcurrencyType

Page 46: Core Data with multiple managed object contexts

Asynchronous Saving - Parent Child Context

Code Examplehttps://github.com/mmorey/CoreDataMultiContext/tree/parent-context

Page 47: Core Data with multiple managed object contexts

Still Blocking?

Page 48: Core Data with multiple managed object contexts

Still Blocking?

Small and frequent saves during import

Page 49: Core Data with multiple managed object contexts

Still Blocking?

Wait for opportunity when user won’t notice

Page 50: Core Data with multiple managed object contexts

Still Blocking?

- (void)setStalenessInterval:(NSTimeInterval)expiration

Page 51: Core Data with multiple managed object contexts

Still Blocking?

NSFetchedResultsController *aFetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest

managedObjectContext:self.managedObjectContext sectionNameKeyPath:@"sections" cacheName:@"default-cache"];

Page 52: Core Data with multiple managed object contexts

Still Blocking?

For VERY LARGE amounts of data it may be better to generate the SQLite file on the

server, download it asynchronously, and set it up as an additional persistent store.

Page 53: Core Data with multiple managed object contexts

References

Nested MOC Release Notes: http://developer.apple.com/library/mac/#releasenotes/DataManagement/RN-CoreData/index.html

Core Data Programming Guide: http://developer.apple.com/library/mac/#documentation/cocoa/Conceptual/CoreData/cdProgrammingGuide.html

Cocoanetics Blog:http://www.cocoanetics.com/2012/07/multi-context-coredata/

http://www.cocoanetics.com/2013/02/zarra-on-locking/

Page 54: Core Data with multiple managed object contexts

Thanks!

Questions? Get in Touch.Twitter: @xzolianApp.net: @morey

Email: [email protected]: http://matthewmorey.com