├── .gitignore ├── MultiThreadedCoreData.xcdatamodeld ├── MultiThreadedCoreData.xcdatamodel │ ├── layout │ └── elements └── .xccurrentversion ├── Person.m ├── Person.h ├── main.m ├── MultiThreadedCoreData_Prefix.pch ├── Classes ├── MergePolicySelectionEditor.h ├── MultiThreadedCoreDataAppDelegate.h ├── UpdateSurnameOperation.h ├── UpdateFirstNameOperation.h ├── RootViewController.h ├── UpdateSurnameOperation.m ├── UpdateFirstNameOperation.m ├── ThreadedCoreDataOperation.h ├── MergePolicySelectionEditor.m ├── ThreadedCoreDataOperation.m ├── MultiThreadedCoreDataAppDelegate.m └── RootViewController.m ├── MultiThreadedCoreData-Info.plist ├── README.md ├── MainWindow.xib ├── MultiThreadedCoreData.xcodeproj └── project.pbxproj └── RootViewController.xib /.gitignore: -------------------------------------------------------------------------------- 1 | build 2 | *.pbxuser 3 | *.mode1v3 4 | -------------------------------------------------------------------------------- /MultiThreadedCoreData.xcdatamodeld/MultiThreadedCoreData.xcdatamodel/layout: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jabley/multi-threaded-core-data/HEAD/MultiThreadedCoreData.xcdatamodeld/MultiThreadedCoreData.xcdatamodel/layout -------------------------------------------------------------------------------- /MultiThreadedCoreData.xcdatamodeld/MultiThreadedCoreData.xcdatamodel/elements: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jabley/multi-threaded-core-data/HEAD/MultiThreadedCoreData.xcdatamodeld/MultiThreadedCoreData.xcdatamodel/elements -------------------------------------------------------------------------------- /Person.m: -------------------------------------------------------------------------------- 1 | // 2 | // Person.m 3 | // MultiThreadedCoreData 4 | // 5 | // Created by James Abley on 04/07/2010. 6 | // Copyright 2010 Mobile IQ Ltd. All rights reserved. 7 | // 8 | 9 | #import "Person.h" 10 | 11 | 12 | @implementation Person 13 | 14 | @dynamic firstName; 15 | @dynamic surname; 16 | 17 | @end 18 | -------------------------------------------------------------------------------- /MultiThreadedCoreData.xcdatamodeld/.xccurrentversion: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | _XCCurrentVersionName 6 | MultiThreadedCoreData.xcdatamodel 7 | 8 | 9 | -------------------------------------------------------------------------------- /Person.h: -------------------------------------------------------------------------------- 1 | // 2 | // Person.h 3 | // MultiThreadedCoreData 4 | // 5 | // Created by James Abley on 04/07/2010. 6 | // Copyright 2010 Mobile IQ Ltd. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | 12 | @interface Person : NSManagedObject 13 | { 14 | } 15 | 16 | @property (nonatomic, retain) NSString * firstName; 17 | @property (nonatomic, retain) NSString * surname; 18 | 19 | @end 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // MultiThreadedCoreData 4 | // 5 | // Created by James Abley on 04/07/2010. 6 | // Copyright Mobile IQ Ltd 2010. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | int main(int argc, char *argv[]) { 12 | 13 | NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; 14 | int retVal = UIApplicationMain(argc, argv, nil, nil); 15 | [pool release]; 16 | return retVal; 17 | } 18 | -------------------------------------------------------------------------------- /MultiThreadedCoreData_Prefix.pch: -------------------------------------------------------------------------------- 1 | // 2 | // Prefix header for all source files of the 'MultiThreadedCoreData' target in the 'MultiThreadedCoreData' project 3 | // 4 | 5 | #import 6 | 7 | #ifndef __IPHONE_3_0 8 | #warning "This project uses features only available in iPhone SDK 3.0 and later." 9 | #endif 10 | 11 | 12 | #ifdef __OBJC__ 13 | #import 14 | #import 15 | #import 16 | #endif 17 | -------------------------------------------------------------------------------- /Classes/MergePolicySelectionEditor.h: -------------------------------------------------------------------------------- 1 | // 2 | // MergePolicySelectionEditor.h 3 | // MultiThreadedCoreData 4 | // 5 | // Created by James Abley on 09/07/2010. 6 | // Copyright 2010 Mobile IQ Ltd. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | 12 | @interface MergePolicySelectionEditor : UITableViewController { 13 | 14 | @private 15 | 16 | /** 17 | The keypath of the target object. 18 | */ 19 | NSString *keypath_; 20 | 21 | /** 22 | The merge policies that can be selected. 23 | */ 24 | NSArray *mergePoliciesList_; 25 | 26 | /** 27 | The target object being modified. 28 | */ 29 | NSObject *target_; 30 | 31 | } 32 | 33 | @property (nonatomic, copy) NSString *keypath; 34 | 35 | @property (nonatomic, retain) NSArray *mergePoliciesList; 36 | 37 | @property (nonatomic, retain) NSObject *target; 38 | 39 | @end 40 | -------------------------------------------------------------------------------- /Classes/MultiThreadedCoreDataAppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // MultiThreadedCoreDataAppDelegate.h 3 | // MultiThreadedCoreData 4 | // 5 | // Created by James Abley on 04/07/2010. 6 | // Copyright Mobile IQ Ltd 2010. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | 12 | @interface MultiThreadedCoreDataAppDelegate : NSObject { 13 | 14 | NSManagedObjectModel *managedObjectModel; 15 | NSManagedObjectContext *managedObjectContext; 16 | NSPersistentStoreCoordinator *persistentStoreCoordinator; 17 | 18 | UIWindow *window; 19 | UINavigationController *navigationController; 20 | } 21 | 22 | @property (nonatomic, retain) IBOutlet UIWindow *window; 23 | @property (nonatomic, retain) IBOutlet UINavigationController *navigationController; 24 | 25 | - (NSString *)applicationDocumentsDirectory; 26 | 27 | @end 28 | 29 | -------------------------------------------------------------------------------- /Classes/UpdateSurnameOperation.h: -------------------------------------------------------------------------------- 1 | // 2 | // UpdateSurnameOperation.h 3 | // MultiThreadedCoreData 4 | // 5 | // Created by James Abley on 09/07/2010. 6 | // Copyright 2010 Mobile IQ Ltd. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "ThreadedCoreDataOperation.h" 11 | 12 | /** 13 | ThreadedCoreDataOperation that will update the surname of a specified Person entity. 14 | */ 15 | @interface UpdateSurnameOperation : ThreadedCoreDataOperation { 16 | 17 | /** 18 | The Person entity to be updated. 19 | */ 20 | NSManagedObjectID *entityID_; // strong 21 | 22 | } 23 | 24 | /** 25 | Creates a new ThreadedCoreDataOperation that will update the surname field of the specified Person entity. 26 | */ 27 | - (id)initWithManagedObjectContext:(NSManagedObjectContext *)mainContext 28 | mergePolicy:(id)mergePolicy 29 | entityID:(NSManagedObjectID*)entityID; 30 | 31 | @end 32 | -------------------------------------------------------------------------------- /Classes/UpdateFirstNameOperation.h: -------------------------------------------------------------------------------- 1 | // 2 | // UpdateFirstNameOperation.h 3 | // MultiThreadedCoreData 4 | // 5 | // Created by James Abley on 09/07/2010. 6 | // Copyright 2010 Mobile IQ Ltd. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "ThreadedCoreDataOperation.h" 11 | 12 | /** 13 | ThreadedCoreDataOperation that will update the firstName of the specified Person entity. 14 | */ 15 | @interface UpdateFirstNameOperation : ThreadedCoreDataOperation { 16 | 17 | @private 18 | /** 19 | The Person entity to be updated. 20 | */ 21 | NSManagedObjectID *entityID_; // strong 22 | 23 | } 24 | 25 | /** 26 | Creates a new ThreadedCoreDataOperation that will update the surname field of the specified Person entity. 27 | */ 28 | - (id)initWithManagedObjectContext:(NSManagedObjectContext *)mainContext 29 | mergePolicy:(id)mergePolicy 30 | entityID:(NSManagedObjectID*)entityID; 31 | 32 | @end 33 | -------------------------------------------------------------------------------- /MultiThreadedCoreData-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | English 7 | CFBundleDisplayName 8 | ${PRODUCT_NAME} 9 | CFBundleExecutable 10 | ${EXECUTABLE_NAME} 11 | CFBundleIconFile 12 | 13 | CFBundleIdentifier 14 | com.yourcompany.${PRODUCT_NAME:rfc1034identifier} 15 | CFBundleInfoDictionaryVersion 16 | 6.0 17 | CFBundleName 18 | ${PRODUCT_NAME} 19 | CFBundlePackageType 20 | APPL 21 | CFBundleSignature 22 | ???? 23 | CFBundleVersion 24 | 1.0 25 | LSRequiresIPhoneOS 26 | 27 | NSMainNibFile 28 | MainWindow 29 | 30 | 31 | -------------------------------------------------------------------------------- /Classes/RootViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // RootViewController.h 3 | // MultiThreadedCoreData 4 | // 5 | // Created by James Abley on 04/07/2010. 6 | // Copyright Mobile IQ Ltd 2010. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | 12 | @interface RootViewController : UITableViewController { 13 | NSFetchedResultsController *fetchedResultsController; 14 | NSManagedObjectContext *managedObjectContext; 15 | 16 | /** 17 | The main NSManagedObjectContext merge policy. 18 | */ 19 | id mainMergePolicy; 20 | 21 | /** 22 | The threaded NSManagedObjectContext merge policy. 23 | */ 24 | id threadedMergePolicy; 25 | 26 | /** 27 | The operation queue to manage the concurrent tasks. 28 | */ 29 | NSOperationQueue *taskQueue_; 30 | 31 | } 32 | 33 | @property (nonatomic, retain) NSFetchedResultsController *fetchedResultsController; 34 | @property (nonatomic, retain) NSManagedObjectContext *managedObjectContext; 35 | 36 | @property (nonatomic, retain) id mainMergePolicy; 37 | @property (nonatomic, retain) id threadedMergePolicy; 38 | 39 | @end 40 | -------------------------------------------------------------------------------- /Classes/UpdateSurnameOperation.m: -------------------------------------------------------------------------------- 1 | // 2 | // UpdateSurnameOperation.m 3 | // MultiThreadedCoreData 4 | // 5 | // Created by James Abley on 09/07/2010. 6 | // Copyright 2010 Mobile IQ Ltd. All rights reserved. 7 | // 8 | 9 | #import "UpdateSurnameOperation.h" 10 | #import "Person.h" 11 | 12 | @implementation UpdateSurnameOperation 13 | 14 | - (id)initWithManagedObjectContext:(NSManagedObjectContext *)mainContext 15 | mergePolicy:(id)mergePolicy 16 | entityID:(NSManagedObjectID *)entityID { 17 | 18 | if (self = [super initWithManagedObjectContext:mainContext mergePolicy:mergePolicy]) { 19 | entityID_ = [entityID retain]; 20 | } 21 | 22 | return self; 23 | } 24 | 25 | - (void) dealloc { 26 | [entityID_ release]; 27 | [super dealloc]; 28 | } 29 | 30 | #pragma mark - 31 | #pragma mark NSOperation 32 | - (void)main { 33 | Person *person = (Person*) [[self threadedContext] objectWithID:entityID_]; 34 | [person setSurname:@"Nixon"]; 35 | 36 | /* Simulate the operation taking a while to complete. */ 37 | [NSThread sleepForTimeInterval:2.0]; 38 | 39 | [self saveThreadedContext]; 40 | } 41 | 42 | @end 43 | -------------------------------------------------------------------------------- /Classes/UpdateFirstNameOperation.m: -------------------------------------------------------------------------------- 1 | // 2 | // UpdateFirstNameOperation.m 3 | // MultiThreadedCoreData 4 | // 5 | // Created by James Abley on 09/07/2010. 6 | // Copyright 2010 Mobile IQ Ltd. All rights reserved. 7 | // 8 | 9 | #import "UpdateFirstNameOperation.h" 10 | #import "Person.h" 11 | 12 | @implementation UpdateFirstNameOperation 13 | 14 | - (id)initWithManagedObjectContext:(NSManagedObjectContext *)mainContext 15 | mergePolicy:(id)mergePolicy 16 | entityID:(NSManagedObjectID *)entityID { 17 | 18 | if (self = [super initWithManagedObjectContext:mainContext mergePolicy:mergePolicy]) { 19 | entityID_ = [entityID retain]; 20 | } 21 | 22 | return self; 23 | } 24 | 25 | - (void) dealloc { 26 | [entityID_ release]; 27 | [super dealloc]; 28 | } 29 | 30 | #pragma mark - 31 | #pragma mark NSOperation 32 | - (void)main { 33 | Person *person = (Person*) [[self threadedContext] objectWithID:entityID_]; 34 | [person setFirstName:@"Thomas"]; 35 | 36 | /* Simulate the operation taking a while to complete. */ 37 | [NSThread sleepForTimeInterval:1.0]; 38 | 39 | [self saveThreadedContext]; 40 | } 41 | 42 | @end 43 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Overview 2 | This is a sample App to demonstrate Core Data multi-threading for safe updates 3 | 4 | The application seeds itself with a Core Data entity and then provides a button to run 2 operations. The NSOperations: 5 | 6 | * update different fields of the same entity 7 | * take a different amount of time to run (intended to simulate distinct differences caused by networking) 8 | * attempt to save their changes. 9 | 10 | The UI allows one to alter the merge policies that are applied to the NSManagedObjectContext used by the application main thread and by the NSOperations. By looking at the console, you can see which combination of merge policies will work best for your application. 11 | 12 | It seems as though the merge policy on the main thread NSManagedObjectContext has no bearing on the outcome of the merge; only the merge policy on the NSManagedObjectContext that performed some changes has any effect. 13 | 14 | For usages where different operations update different fields, it is suggested that NSMergeByPropertyObjectTrumpMergePolicy would be a good option. 15 | For other usages, one would need to balance up the requirements of the application and consider ways of partitioning updates. 16 | -------------------------------------------------------------------------------- /Classes/ThreadedCoreDataOperation.h: -------------------------------------------------------------------------------- 1 | // 2 | // ThreadedCoreDataOperation.h 3 | // MultiThreadedCoreData 4 | // 5 | // Created by James Abley on 09/07/2010. 6 | // Copyright 2010 Mobile IQ Ltd. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | 12 | @interface ThreadedCoreDataOperation : NSOperation { 13 | 14 | NSManagedObjectContext *mainContext_; 15 | 16 | NSManagedObjectContext *threadedContext_; 17 | 18 | /** 19 | The merge policy to use for this Core Data operation. 20 | */ 21 | id mergePolicy_; 22 | } 23 | /** 24 | This is the NSManagedObjectContext intended to be used by 25 | instances of this class for reading and writing to Core Data. 26 | */ 27 | @property (nonatomic, readonly, retain) NSManagedObjectContext *threadedContext; 28 | 29 | /** 30 | Returns the NSManagedObjectContext from the main thread that any updates should be merged into. 31 | */ 32 | @property (nonatomic, retain, readonly) NSManagedObjectContext * mainContext; 33 | 34 | /** 35 | Saves the threaded context, merging the changes into the main context. 36 | */ 37 | - (void)saveThreadedContext; 38 | 39 | /** 40 | Returns a non-nil MIQCoreDataOperation which will merge any changes that this NSOperation makes into the specified 41 | NSManagedObjectContext. 42 | @param moc - non-nil NSManagedObjectContext into which any changes will be merged 43 | @param mergePolicy - non-nil merge policy that the NSManagedObjectContext for this NSOperation will use 44 | */ 45 | - (id)initWithManagedObjectContext:(NSManagedObjectContext*)mainContext mergePolicy:(id)mergePolicy; 46 | 47 | @end 48 | -------------------------------------------------------------------------------- /Classes/MergePolicySelectionEditor.m: -------------------------------------------------------------------------------- 1 | // 2 | // MergePolicySelectionEditor.m 3 | // MultiThreadedCoreData 4 | // 5 | // Created by James Abley on 09/07/2010. 6 | // Copyright 2010 Mobile IQ Ltd. All rights reserved. 7 | // 8 | 9 | #import "MergePolicySelectionEditor.h" 10 | 11 | 12 | @implementation MergePolicySelectionEditor 13 | 14 | @synthesize keypath = keypath_; 15 | @synthesize mergePoliciesList = mergePoliciesList_; 16 | @synthesize target = target_; 17 | 18 | #pragma mark - 19 | #pragma mark Initialization 20 | 21 | #pragma mark - 22 | #pragma mark View lifecycle 23 | 24 | 25 | #pragma mark - 26 | #pragma mark Table view data source 27 | 28 | - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView { 29 | // Return the number of sections. 30 | return 1; 31 | } 32 | 33 | 34 | - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { 35 | // Return the number of rows in the section. 36 | return [mergePoliciesList_ count]; 37 | } 38 | 39 | 40 | // Customize the appearance of table view cells. 41 | - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { 42 | 43 | static NSString *CellIdentifier = @"Cell"; 44 | 45 | UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier]; 46 | if (cell == nil) { 47 | cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease]; 48 | } 49 | 50 | // Configure the cell... 51 | id mergePolicy = [mergePoliciesList_ objectAtIndex:[indexPath row]]; 52 | 53 | [[cell textLabel] setText:[target_ mergePolicyName:mergePolicy]]; 54 | 55 | if ([mergePolicy isEqual:[target_ valueForKey:keypath_]]) { 56 | [cell setAccessoryType:UITableViewCellAccessoryCheckmark]; 57 | } else { 58 | [cell setAccessoryType:UITableViewCellAccessoryNone]; 59 | } 60 | 61 | return cell; 62 | } 63 | 64 | #pragma mark - 65 | #pragma mark Table view delegate 66 | 67 | - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { 68 | // Navigation logic may go here. Create and push another view controller. 69 | id selectedMergePolicy = [mergePoliciesList_ objectAtIndex:[indexPath row]]; 70 | [target_ setValue:selectedMergePolicy forKey:keypath_]; 71 | [[self navigationController] popViewControllerAnimated:YES]; 72 | } 73 | 74 | 75 | #pragma mark - 76 | #pragma mark Memory management 77 | 78 | - (void)didReceiveMemoryWarning { 79 | // Releases the view if it doesn't have a superview. 80 | [super didReceiveMemoryWarning]; 81 | 82 | // Relinquish ownership any cached data, images, etc that aren't in use. 83 | } 84 | 85 | - (void)dealloc { 86 | [keypath_ release]; 87 | [mergePoliciesList_ release]; 88 | [target_ release]; 89 | 90 | [super dealloc]; 91 | } 92 | 93 | 94 | @end 95 | 96 | -------------------------------------------------------------------------------- /Classes/ThreadedCoreDataOperation.m: -------------------------------------------------------------------------------- 1 | // 2 | // ThreadedCoreDataOperation.m 3 | // MultiThreadedCoreData 4 | // 5 | // Created by James Abley on 09/07/2010. 6 | // Copyright 2010 Mobile IQ Ltd. All rights reserved. 7 | // 8 | 9 | #import "ThreadedCoreDataOperation.h" 10 | 11 | /** 12 | Category for private API of this class. 13 | */ 14 | @interface ThreadedCoreDataOperation(PrivateMethods) 15 | 16 | /** 17 | Selector called when the threaded context is saved (registered and unregistered for notifications) which is responsible 18 | for merging the threaded context changes into the main thread context. 19 | @param notification - notification 20 | */ 21 | - (void)mergeThreadedContextChangesIntoMainContext:(NSNotification *)notification; 22 | 23 | @end 24 | 25 | @implementation ThreadedCoreDataOperation 26 | 27 | @synthesize mainContext = mainContext_; 28 | @synthesize threadedContext = threadedContext_; 29 | 30 | - (id)initWithManagedObjectContext:(NSManagedObjectContext *)mainContext mergePolicy:(id)mergePolicy { 31 | if (self = [super init]) { 32 | mainContext_ = [mainContext retain]; 33 | mergePolicy_ = [mergePolicy retain]; 34 | } 35 | 36 | return self; 37 | } 38 | 39 | - (void) dealloc { 40 | [mainContext_ release]; 41 | [threadedContext_ release]; 42 | [mergePolicy_ release]; 43 | 44 | [super dealloc]; 45 | } 46 | 47 | #pragma mark - 48 | #pragma mark ThreadedCoreDataOperation 49 | - (NSManagedObjectContext*)threadedContext { 50 | if (!threadedContext_) { 51 | threadedContext_ = [[NSManagedObjectContext alloc] init]; 52 | [threadedContext_ setPersistentStoreCoordinator:[mainContext_ persistentStoreCoordinator]]; 53 | [threadedContext_ setMergePolicy:mergePolicy_]; 54 | } 55 | 56 | return [[threadedContext_ retain] autorelease]; 57 | } 58 | 59 | - (void)saveThreadedContext { 60 | NSNotificationCenter *defaultCenter = [NSNotificationCenter defaultCenter]; 61 | 62 | [defaultCenter addObserver:self 63 | selector:@selector(mergeThreadedContextChangesIntoMainContext:) 64 | name:NSManagedObjectContextDidSaveNotification 65 | object:self.threadedContext]; 66 | 67 | if ([[self threadedContext] hasChanges]) { 68 | 69 | NSError *error; 70 | 71 | BOOL contextDidSave = [[self threadedContext] save:&error]; 72 | 73 | if (!contextDidSave) { 74 | 75 | // If the context failed to save, log out as many details as possible. 76 | NSLog(@"Failed to save to data store: %@", [error localizedDescription]); 77 | 78 | NSArray* detailedErrors = [[error userInfo] objectForKey:NSDetailedErrorsKey]; 79 | 80 | if (detailedErrors != nil && [detailedErrors count] > 0) { 81 | 82 | for (NSError* detailedError in detailedErrors) { 83 | NSLog(@" DetailedError: %@", [detailedError userInfo]); 84 | } 85 | } else { 86 | NSLog(@" %@", [error userInfo]); 87 | } 88 | } 89 | } 90 | 91 | [defaultCenter removeObserver:self name:NSManagedObjectContextDidSaveNotification object:[self threadedContext]]; 92 | } 93 | 94 | #pragma mark - 95 | #pragma mark PrivateMethods 96 | - (void)mergeThreadedContextChangesIntoMainContext:(NSNotification *)notification { 97 | NSLog(@"%@:%@ merging changes", self, NSStringFromSelector(_cmd)); 98 | [mainContext_ performSelectorOnMainThread:@selector(mergeChangesFromContextDidSaveNotification:) 99 | withObject:notification 100 | waitUntilDone:YES]; 101 | } 102 | 103 | 104 | @end 105 | -------------------------------------------------------------------------------- /Classes/MultiThreadedCoreDataAppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // MultiThreadedCoreDataAppDelegate.m 3 | // MultiThreadedCoreData 4 | // 5 | // Created by James Abley on 04/07/2010. 6 | // Copyright Mobile IQ Ltd 2010. All rights reserved. 7 | // 8 | 9 | #import "MultiThreadedCoreDataAppDelegate.h" 10 | #import "RootViewController.h" 11 | 12 | 13 | @interface MultiThreadedCoreDataAppDelegate (PrivateCoreDataStack) 14 | @property (nonatomic, retain, readonly) NSManagedObjectModel *managedObjectModel; 15 | @property (nonatomic, retain, readonly) NSManagedObjectContext *managedObjectContext; 16 | @property (nonatomic, retain, readonly) NSPersistentStoreCoordinator *persistentStoreCoordinator; 17 | @end 18 | 19 | 20 | @implementation MultiThreadedCoreDataAppDelegate 21 | 22 | @synthesize window; 23 | @synthesize navigationController; 24 | 25 | 26 | #pragma mark - 27 | #pragma mark Application lifecycle 28 | 29 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { 30 | 31 | // Override point for customization after app launch 32 | 33 | RootViewController *rootViewController = (RootViewController *)[navigationController topViewController]; 34 | rootViewController.managedObjectContext = self.managedObjectContext; 35 | 36 | [window addSubview:[navigationController view]]; 37 | [window makeKeyAndVisible]; 38 | return YES; 39 | } 40 | 41 | /** 42 | applicationWillTerminate: saves changes in the application's managed object context before the application terminates. 43 | */ 44 | - (void)applicationWillTerminate:(UIApplication *)application { 45 | 46 | NSError *error = nil; 47 | if (managedObjectContext != nil) { 48 | if ([managedObjectContext hasChanges] && ![managedObjectContext save:&error]) { 49 | /* 50 | Replace this implementation with code to handle the error appropriately. 51 | 52 | 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. If it is not possible to recover from the error, display an alert panel that instructs the user to quit the application by pressing the Home button. 53 | */ 54 | NSLog(@"Unresolved error %@, %@", error, [error userInfo]); 55 | abort(); 56 | } 57 | } 58 | } 59 | 60 | 61 | #pragma mark - 62 | #pragma mark Core Data stack 63 | 64 | /** 65 | Returns the managed object context for the application. 66 | If the context doesn't already exist, it is created and bound to the persistent store coordinator for the application. 67 | */ 68 | - (NSManagedObjectContext *) managedObjectContext { 69 | 70 | if (managedObjectContext != nil) { 71 | return managedObjectContext; 72 | } 73 | 74 | NSPersistentStoreCoordinator *coordinator = [self persistentStoreCoordinator]; 75 | if (coordinator != nil) { 76 | managedObjectContext = [[NSManagedObjectContext alloc] init]; 77 | [managedObjectContext setPersistentStoreCoordinator: coordinator]; 78 | } 79 | return managedObjectContext; 80 | } 81 | 82 | 83 | /** 84 | Returns the managed object model for the application. 85 | If the model doesn't already exist, it is created by merging all of the models found in the application bundle. 86 | */ 87 | - (NSManagedObjectModel *)managedObjectModel { 88 | 89 | if (managedObjectModel != nil) { 90 | return managedObjectModel; 91 | } 92 | managedObjectModel = [[NSManagedObjectModel mergedModelFromBundles:nil] retain]; 93 | return managedObjectModel; 94 | } 95 | 96 | 97 | /** 98 | Returns the persistent store coordinator for the application. 99 | If the coordinator doesn't already exist, it is created and the application's store added to it. 100 | */ 101 | - (NSPersistentStoreCoordinator *)persistentStoreCoordinator { 102 | 103 | if (persistentStoreCoordinator != nil) { 104 | return persistentStoreCoordinator; 105 | } 106 | 107 | NSURL *storeUrl = [NSURL fileURLWithPath: [[self applicationDocumentsDirectory] stringByAppendingPathComponent: @"MultiThreadedCoreData.sqlite"]]; 108 | 109 | NSError *error = nil; 110 | persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:[self managedObjectModel]]; 111 | if (![persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeUrl options:nil error:&error]) { 112 | /* 113 | Replace this implementation with code to handle the error appropriately. 114 | 115 | 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. If it is not possible to recover from the error, display an alert panel that instructs the user to quit the application by pressing the Home button. 116 | 117 | Typical reasons for an error here include: 118 | * The persistent store is not accessible 119 | * The schema for the persistent store is incompatible with current managed object model 120 | Check the error message to determine what the actual problem was. 121 | */ 122 | NSLog(@"Unresolved error %@, %@", error, [error userInfo]); 123 | abort(); 124 | } 125 | 126 | return persistentStoreCoordinator; 127 | } 128 | 129 | 130 | #pragma mark - 131 | #pragma mark Application's Documents directory 132 | 133 | /** 134 | Returns the path to the application's Documents directory. 135 | */ 136 | - (NSString *)applicationDocumentsDirectory { 137 | return [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject]; 138 | } 139 | 140 | 141 | #pragma mark - 142 | #pragma mark Memory management 143 | 144 | - (void)dealloc { 145 | 146 | [managedObjectContext release]; 147 | [managedObjectModel release]; 148 | [persistentStoreCoordinator release]; 149 | 150 | [navigationController release]; 151 | [window release]; 152 | [super dealloc]; 153 | } 154 | 155 | 156 | @end 157 | 158 | -------------------------------------------------------------------------------- /MainWindow.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 800 5 | 10D541 6 | 760 7 | 1038.29 8 | 460.00 9 | 10 | com.apple.InterfaceBuilder.IBCocoaTouchPlugin 11 | 81 12 | 13 | 14 | YES 15 | 16 | 17 | 18 | YES 19 | com.apple.InterfaceBuilder.IBCocoaTouchPlugin 20 | 21 | 22 | YES 23 | 24 | YES 25 | 26 | 27 | YES 28 | 29 | 30 | 31 | YES 32 | 33 | IBFilesOwner 34 | IBCocoaTouchFramework 35 | 36 | 37 | IBFirstResponder 38 | IBCocoaTouchFramework 39 | 40 | 41 | IBCocoaTouchFramework 42 | 43 | 44 | 45 | 1316 46 | 47 | {320, 480} 48 | 49 | 1 50 | MSAxIDEAA 51 | 52 | NO 53 | NO 54 | 55 | IBCocoaTouchFramework 56 | YES 57 | 58 | 59 | 60 | IBCocoaTouchFramework 61 | 62 | 63 | 256 64 | {0, 0} 65 | NO 66 | YES 67 | YES 68 | IBCocoaTouchFramework 69 | 70 | 71 | YES 72 | 73 | 74 | 75 | IBCocoaTouchFramework 76 | 77 | 78 | RootViewController 79 | 80 | IBCocoaTouchFramework 81 | 82 | 83 | 84 | 85 | 86 | 87 | YES 88 | 89 | 90 | delegate 91 | 92 | 93 | 94 | 4 95 | 96 | 97 | 98 | window 99 | 100 | 101 | 102 | 5 103 | 104 | 105 | 106 | navigationController 107 | 108 | 109 | 110 | 15 111 | 112 | 113 | 114 | 115 | YES 116 | 117 | 0 118 | 119 | 120 | 121 | 122 | 123 | 2 124 | 125 | 126 | YES 127 | 128 | 129 | 130 | 131 | -1 132 | 133 | 134 | File's Owner 135 | 136 | 137 | 3 138 | 139 | 140 | 141 | 142 | -2 143 | 144 | 145 | 146 | 147 | 9 148 | 149 | 150 | YES 151 | 152 | 153 | 154 | 155 | 156 | 157 | 11 158 | 159 | 160 | 161 | 162 | 13 163 | 164 | 165 | YES 166 | 167 | 168 | 169 | 170 | 171 | 14 172 | 173 | 174 | 175 | 176 | 177 | 178 | YES 179 | 180 | YES 181 | -1.CustomClassName 182 | -2.CustomClassName 183 | 11.IBPluginDependency 184 | 13.CustomClassName 185 | 13.IBPluginDependency 186 | 2.IBAttributePlaceholdersKey 187 | 2.IBEditorWindowLastContentRect 188 | 2.IBPluginDependency 189 | 3.CustomClassName 190 | 3.IBPluginDependency 191 | 9.IBEditorWindowLastContentRect 192 | 9.IBPluginDependency 193 | 194 | 195 | YES 196 | UIApplication 197 | UIResponder 198 | com.apple.InterfaceBuilder.IBCocoaTouchPlugin 199 | RootViewController 200 | com.apple.InterfaceBuilder.IBCocoaTouchPlugin 201 | 202 | YES 203 | 204 | 205 | YES 206 | 207 | 208 | {{673, 376}, {320, 480}} 209 | com.apple.InterfaceBuilder.IBCocoaTouchPlugin 210 | MultiThreadedCoreDataAppDelegate 211 | com.apple.InterfaceBuilder.IBCocoaTouchPlugin 212 | {{190, 376}, {320, 480}} 213 | com.apple.InterfaceBuilder.IBCocoaTouchPlugin 214 | 215 | 216 | 217 | YES 218 | 219 | 220 | YES 221 | 222 | 223 | 224 | 225 | YES 226 | 227 | 228 | YES 229 | 230 | 231 | 232 | 15 233 | 234 | 235 | 236 | YES 237 | 238 | RootViewController 239 | UITableViewController 240 | 241 | IBProjectSource 242 | Classes/RootViewController.h 243 | 244 | 245 | 246 | MultiThreadedCoreDataAppDelegate 247 | NSObject 248 | 249 | YES 250 | 251 | YES 252 | navigationController 253 | window 254 | 255 | 256 | YES 257 | UINavigationController 258 | UIWindow 259 | 260 | 261 | 262 | IBProjectSource 263 | Classes/MultiThreadedCoreDataAppDelegate.h 264 | 265 | 266 | 267 | 268 | 0 269 | IBCocoaTouchFramework 270 | 271 | com.apple.InterfaceBuilder.CocoaTouchPlugin.InterfaceBuilder3 272 | 273 | 274 | YES 275 | MultiThreadedCoreData.xcodeproj 276 | 3 277 | 81 278 | 279 | 280 | -------------------------------------------------------------------------------- /MultiThreadedCoreData.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 45; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 1D3623260D0F684500981E51 /* MultiThreadedCoreDataAppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 1D3623250D0F684500981E51 /* MultiThreadedCoreDataAppDelegate.m */; }; 11 | 1D60589B0D05DD56006BFB54 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 29B97316FDCFA39411CA2CEA /* main.m */; }; 12 | 1D60589F0D05DD5A006BFB54 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1D30AB110D05D00D00671497 /* Foundation.framework */; }; 13 | 1DF5F4E00D08C38300B7A737 /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1DF5F4DF0D08C38300B7A737 /* UIKit.framework */; }; 14 | 28860BE50F44EE6400985440 /* CoreData.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 28860BE40F44EE6400985440 /* CoreData.framework */; }; 15 | 2892E4100DC94CBA00A64D0F /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 2892E40F0DC94CBA00A64D0F /* CoreGraphics.framework */; }; 16 | 2899E5600DE3E45000AC0155 /* RootViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 2899E55F0DE3E45000AC0155 /* RootViewController.xib */; }; 17 | 28AD73600D9D9599002E5188 /* MainWindow.xib in Resources */ = {isa = PBXBuildFile; fileRef = 28AD735F0D9D9599002E5188 /* MainWindow.xib */; }; 18 | 28C286E10D94DF7D0034E888 /* RootViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 28C286E00D94DF7D0034E888 /* RootViewController.m */; }; 19 | 28D3F202112F7DC200FD0661 /* MultiThreadedCoreData.xcdatamodeld in Sources */ = {isa = PBXBuildFile; fileRef = 28D3F201112F7DC200FD0661 /* MultiThreadedCoreData.xcdatamodeld */; }; 20 | F879AFCF11E7283100F2EB2E /* ThreadedCoreDataOperation.m in Sources */ = {isa = PBXBuildFile; fileRef = F879AFCE11E7283100F2EB2E /* ThreadedCoreDataOperation.m */; }; 21 | F879AFF511E72B9700F2EB2E /* UpdateFirstNameOperation.m in Sources */ = {isa = PBXBuildFile; fileRef = F879AFF411E72B9700F2EB2E /* UpdateFirstNameOperation.m */; }; 22 | F879AFF811E72BA500F2EB2E /* UpdateSurnameOperation.m in Sources */ = {isa = PBXBuildFile; fileRef = F879AFF711E72BA500F2EB2E /* UpdateSurnameOperation.m */; }; 23 | F879B05711E736F000F2EB2E /* MergePolicySelectionEditor.m in Sources */ = {isa = PBXBuildFile; fileRef = F879B05611E736F000F2EB2E /* MergePolicySelectionEditor.m */; }; 24 | F8F6170E11E1188000C54014 /* Person.m in Sources */ = {isa = PBXBuildFile; fileRef = F8F6170D11E1188000C54014 /* Person.m */; }; 25 | /* End PBXBuildFile section */ 26 | 27 | /* Begin PBXFileReference section */ 28 | 1D30AB110D05D00D00671497 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; 29 | 1D3623240D0F684500981E51 /* MultiThreadedCoreDataAppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MultiThreadedCoreDataAppDelegate.h; sourceTree = ""; }; 30 | 1D3623250D0F684500981E51 /* MultiThreadedCoreDataAppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MultiThreadedCoreDataAppDelegate.m; sourceTree = ""; }; 31 | 1D6058910D05DD3D006BFB54 /* MultiThreadedCoreData.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = MultiThreadedCoreData.app; sourceTree = BUILT_PRODUCTS_DIR; }; 32 | 1DF5F4DF0D08C38300B7A737 /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; }; 33 | 28860B760F44E54D00985440 /* MultiThreadedCoreData.xcdatamodel */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = wrapper.xcdatamodel; path = MultiThreadedCoreData.xcdatamodel; sourceTree = ""; }; 34 | 28860BE40F44EE6400985440 /* CoreData.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreData.framework; path = System/Library/Frameworks/CoreData.framework; sourceTree = SDKROOT; }; 35 | 2892E40F0DC94CBA00A64D0F /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = System/Library/Frameworks/CoreGraphics.framework; sourceTree = SDKROOT; }; 36 | 2899E55F0DE3E45000AC0155 /* RootViewController.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = RootViewController.xib; sourceTree = ""; }; 37 | 28A0AAE50D9B0CCF005BE974 /* MultiThreadedCoreData_Prefix.pch */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MultiThreadedCoreData_Prefix.pch; sourceTree = ""; }; 38 | 28AD735F0D9D9599002E5188 /* MainWindow.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = MainWindow.xib; sourceTree = ""; }; 39 | 28C286DF0D94DF7D0034E888 /* RootViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RootViewController.h; sourceTree = ""; }; 40 | 28C286E00D94DF7D0034E888 /* RootViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RootViewController.m; sourceTree = ""; }; 41 | 29B97316FDCFA39411CA2CEA /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 42 | 8D1107310486CEB800E47090 /* MultiThreadedCoreData-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "MultiThreadedCoreData-Info.plist"; plistStructureDefinitionIdentifier = "com.apple.xcode.plist.structure-definition.iphone.info-plist"; sourceTree = ""; }; 43 | F879AFCD11E7283100F2EB2E /* ThreadedCoreDataOperation.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ThreadedCoreDataOperation.h; sourceTree = ""; }; 44 | F879AFCE11E7283100F2EB2E /* ThreadedCoreDataOperation.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ThreadedCoreDataOperation.m; sourceTree = ""; }; 45 | F879AFF311E72B9700F2EB2E /* UpdateFirstNameOperation.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = UpdateFirstNameOperation.h; sourceTree = ""; }; 46 | F879AFF411E72B9700F2EB2E /* UpdateFirstNameOperation.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = UpdateFirstNameOperation.m; sourceTree = ""; }; 47 | F879AFF611E72BA500F2EB2E /* UpdateSurnameOperation.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = UpdateSurnameOperation.h; sourceTree = ""; }; 48 | F879AFF711E72BA500F2EB2E /* UpdateSurnameOperation.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = UpdateSurnameOperation.m; sourceTree = ""; }; 49 | F879B05511E736F000F2EB2E /* MergePolicySelectionEditor.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MergePolicySelectionEditor.h; sourceTree = ""; }; 50 | F879B05611E736F000F2EB2E /* MergePolicySelectionEditor.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MergePolicySelectionEditor.m; sourceTree = ""; }; 51 | F8F6170C11E1187F00C54014 /* Person.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Person.h; sourceTree = SOURCE_ROOT; }; 52 | F8F6170D11E1188000C54014 /* Person.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = Person.m; sourceTree = SOURCE_ROOT; }; 53 | /* End PBXFileReference section */ 54 | 55 | /* Begin PBXFrameworksBuildPhase section */ 56 | 1D60588F0D05DD3D006BFB54 /* Frameworks */ = { 57 | isa = PBXFrameworksBuildPhase; 58 | buildActionMask = 2147483647; 59 | files = ( 60 | 1D60589F0D05DD5A006BFB54 /* Foundation.framework in Frameworks */, 61 | 1DF5F4E00D08C38300B7A737 /* UIKit.framework in Frameworks */, 62 | 2892E4100DC94CBA00A64D0F /* CoreGraphics.framework in Frameworks */, 63 | 28860BE50F44EE6400985440 /* CoreData.framework in Frameworks */, 64 | ); 65 | runOnlyForDeploymentPostprocessing = 0; 66 | }; 67 | /* End PBXFrameworksBuildPhase section */ 68 | 69 | /* Begin PBXGroup section */ 70 | 080E96DDFE201D6D7F000001 /* Classes */ = { 71 | isa = PBXGroup; 72 | children = ( 73 | F879AFC811E7274300F2EB2E /* Model */, 74 | F879AFCC11E727EB00F2EB2E /* Operations */, 75 | F879AFC911E7275100F2EB2E /* View Controllers */, 76 | 1D3623240D0F684500981E51 /* MultiThreadedCoreDataAppDelegate.h */, 77 | 1D3623250D0F684500981E51 /* MultiThreadedCoreDataAppDelegate.m */, 78 | ); 79 | path = Classes; 80 | sourceTree = ""; 81 | }; 82 | 19C28FACFE9D520D11CA2CBB /* Products */ = { 83 | isa = PBXGroup; 84 | children = ( 85 | 1D6058910D05DD3D006BFB54 /* MultiThreadedCoreData.app */, 86 | ); 87 | name = Products; 88 | sourceTree = ""; 89 | }; 90 | 29B97314FDCFA39411CA2CEA /* CustomTemplate */ = { 91 | isa = PBXGroup; 92 | children = ( 93 | 080E96DDFE201D6D7F000001 /* Classes */, 94 | 29B97315FDCFA39411CA2CEA /* Other Sources */, 95 | 29B97317FDCFA39411CA2CEA /* Resources */, 96 | 29B97323FDCFA39411CA2CEA /* Frameworks */, 97 | 19C28FACFE9D520D11CA2CBB /* Products */, 98 | ); 99 | name = CustomTemplate; 100 | sourceTree = ""; 101 | }; 102 | 29B97315FDCFA39411CA2CEA /* Other Sources */ = { 103 | isa = PBXGroup; 104 | children = ( 105 | 28A0AAE50D9B0CCF005BE974 /* MultiThreadedCoreData_Prefix.pch */, 106 | 29B97316FDCFA39411CA2CEA /* main.m */, 107 | ); 108 | name = "Other Sources"; 109 | sourceTree = ""; 110 | }; 111 | 29B97317FDCFA39411CA2CEA /* Resources */ = { 112 | isa = PBXGroup; 113 | children = ( 114 | 28D3F201112F7DC200FD0661 /* MultiThreadedCoreData.xcdatamodeld */, 115 | 2899E55F0DE3E45000AC0155 /* RootViewController.xib */, 116 | 28AD735F0D9D9599002E5188 /* MainWindow.xib */, 117 | 8D1107310486CEB800E47090 /* MultiThreadedCoreData-Info.plist */, 118 | ); 119 | name = Resources; 120 | sourceTree = ""; 121 | }; 122 | 29B97323FDCFA39411CA2CEA /* Frameworks */ = { 123 | isa = PBXGroup; 124 | children = ( 125 | 1DF5F4DF0D08C38300B7A737 /* UIKit.framework */, 126 | 1D30AB110D05D00D00671497 /* Foundation.framework */, 127 | 2892E40F0DC94CBA00A64D0F /* CoreGraphics.framework */, 128 | 28860BE40F44EE6400985440 /* CoreData.framework */, 129 | ); 130 | name = Frameworks; 131 | sourceTree = ""; 132 | }; 133 | F879AFC811E7274300F2EB2E /* Model */ = { 134 | isa = PBXGroup; 135 | children = ( 136 | F8F6170C11E1187F00C54014 /* Person.h */, 137 | F8F6170D11E1188000C54014 /* Person.m */, 138 | ); 139 | name = Model; 140 | sourceTree = ""; 141 | }; 142 | F879AFC911E7275100F2EB2E /* View Controllers */ = { 143 | isa = PBXGroup; 144 | children = ( 145 | 28C286DF0D94DF7D0034E888 /* RootViewController.h */, 146 | 28C286E00D94DF7D0034E888 /* RootViewController.m */, 147 | F879B05511E736F000F2EB2E /* MergePolicySelectionEditor.h */, 148 | F879B05611E736F000F2EB2E /* MergePolicySelectionEditor.m */, 149 | ); 150 | name = "View Controllers"; 151 | sourceTree = ""; 152 | }; 153 | F879AFCC11E727EB00F2EB2E /* Operations */ = { 154 | isa = PBXGroup; 155 | children = ( 156 | F879AFCD11E7283100F2EB2E /* ThreadedCoreDataOperation.h */, 157 | F879AFCE11E7283100F2EB2E /* ThreadedCoreDataOperation.m */, 158 | F879AFF311E72B9700F2EB2E /* UpdateFirstNameOperation.h */, 159 | F879AFF411E72B9700F2EB2E /* UpdateFirstNameOperation.m */, 160 | F879AFF611E72BA500F2EB2E /* UpdateSurnameOperation.h */, 161 | F879AFF711E72BA500F2EB2E /* UpdateSurnameOperation.m */, 162 | ); 163 | name = Operations; 164 | sourceTree = ""; 165 | }; 166 | /* End PBXGroup section */ 167 | 168 | /* Begin PBXNativeTarget section */ 169 | 1D6058900D05DD3D006BFB54 /* MultiThreadedCoreData */ = { 170 | isa = PBXNativeTarget; 171 | buildConfigurationList = 1D6058960D05DD3E006BFB54 /* Build configuration list for PBXNativeTarget "MultiThreadedCoreData" */; 172 | buildPhases = ( 173 | 1D60588D0D05DD3D006BFB54 /* Resources */, 174 | 1D60588E0D05DD3D006BFB54 /* Sources */, 175 | 1D60588F0D05DD3D006BFB54 /* Frameworks */, 176 | ); 177 | buildRules = ( 178 | ); 179 | dependencies = ( 180 | ); 181 | name = MultiThreadedCoreData; 182 | productName = MultiThreadedCoreData; 183 | productReference = 1D6058910D05DD3D006BFB54 /* MultiThreadedCoreData.app */; 184 | productType = "com.apple.product-type.application"; 185 | }; 186 | /* End PBXNativeTarget section */ 187 | 188 | /* Begin PBXProject section */ 189 | 29B97313FDCFA39411CA2CEA /* Project object */ = { 190 | isa = PBXProject; 191 | buildConfigurationList = C01FCF4E08A954540054247B /* Build configuration list for PBXProject "MultiThreadedCoreData" */; 192 | compatibilityVersion = "Xcode 3.1"; 193 | hasScannedForEncodings = 1; 194 | knownRegions = ( 195 | English, 196 | Japanese, 197 | French, 198 | German, 199 | en, 200 | ); 201 | mainGroup = 29B97314FDCFA39411CA2CEA /* CustomTemplate */; 202 | projectDirPath = ""; 203 | projectRoot = ""; 204 | targets = ( 205 | 1D6058900D05DD3D006BFB54 /* MultiThreadedCoreData */, 206 | ); 207 | }; 208 | /* End PBXProject section */ 209 | 210 | /* Begin PBXResourcesBuildPhase section */ 211 | 1D60588D0D05DD3D006BFB54 /* Resources */ = { 212 | isa = PBXResourcesBuildPhase; 213 | buildActionMask = 2147483647; 214 | files = ( 215 | 28AD73600D9D9599002E5188 /* MainWindow.xib in Resources */, 216 | 2899E5600DE3E45000AC0155 /* RootViewController.xib in Resources */, 217 | ); 218 | runOnlyForDeploymentPostprocessing = 0; 219 | }; 220 | /* End PBXResourcesBuildPhase section */ 221 | 222 | /* Begin PBXSourcesBuildPhase section */ 223 | 1D60588E0D05DD3D006BFB54 /* Sources */ = { 224 | isa = PBXSourcesBuildPhase; 225 | buildActionMask = 2147483647; 226 | files = ( 227 | 1D60589B0D05DD56006BFB54 /* main.m in Sources */, 228 | 1D3623260D0F684500981E51 /* MultiThreadedCoreDataAppDelegate.m in Sources */, 229 | 28C286E10D94DF7D0034E888 /* RootViewController.m in Sources */, 230 | 28D3F202112F7DC200FD0661 /* MultiThreadedCoreData.xcdatamodeld in Sources */, 231 | F8F6170E11E1188000C54014 /* Person.m in Sources */, 232 | F879AFCF11E7283100F2EB2E /* ThreadedCoreDataOperation.m in Sources */, 233 | F879AFF511E72B9700F2EB2E /* UpdateFirstNameOperation.m in Sources */, 234 | F879AFF811E72BA500F2EB2E /* UpdateSurnameOperation.m in Sources */, 235 | F879B05711E736F000F2EB2E /* MergePolicySelectionEditor.m in Sources */, 236 | ); 237 | runOnlyForDeploymentPostprocessing = 0; 238 | }; 239 | /* End PBXSourcesBuildPhase section */ 240 | 241 | /* Begin XCBuildConfiguration section */ 242 | 1D6058940D05DD3E006BFB54 /* Debug */ = { 243 | isa = XCBuildConfiguration; 244 | buildSettings = { 245 | ALWAYS_SEARCH_USER_PATHS = NO; 246 | COPY_PHASE_STRIP = NO; 247 | GCC_DYNAMIC_NO_PIC = NO; 248 | GCC_OPTIMIZATION_LEVEL = 0; 249 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 250 | GCC_PREFIX_HEADER = MultiThreadedCoreData_Prefix.pch; 251 | INFOPLIST_FILE = "MultiThreadedCoreData-Info.plist"; 252 | PRODUCT_NAME = MultiThreadedCoreData; 253 | }; 254 | name = Debug; 255 | }; 256 | 1D6058950D05DD3E006BFB54 /* Release */ = { 257 | isa = XCBuildConfiguration; 258 | buildSettings = { 259 | ALWAYS_SEARCH_USER_PATHS = NO; 260 | COPY_PHASE_STRIP = YES; 261 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 262 | GCC_PREFIX_HEADER = MultiThreadedCoreData_Prefix.pch; 263 | INFOPLIST_FILE = "MultiThreadedCoreData-Info.plist"; 264 | PRODUCT_NAME = MultiThreadedCoreData; 265 | VALIDATE_PRODUCT = YES; 266 | }; 267 | name = Release; 268 | }; 269 | C01FCF4F08A954540054247B /* Debug */ = { 270 | isa = XCBuildConfiguration; 271 | buildSettings = { 272 | ARCHS = "$(ARCHS_STANDARD_32_BIT)"; 273 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 274 | GCC_C_LANGUAGE_STANDARD = c99; 275 | GCC_WARN_ABOUT_RETURN_TYPE = YES; 276 | GCC_WARN_UNUSED_VARIABLE = YES; 277 | IPHONEOS_DEPLOYMENT_TARGET = 3.1; 278 | PREBINDING = NO; 279 | SDKROOT = iphoneos4.0; 280 | }; 281 | name = Debug; 282 | }; 283 | C01FCF5008A954540054247B /* Release */ = { 284 | isa = XCBuildConfiguration; 285 | buildSettings = { 286 | ARCHS = "$(ARCHS_STANDARD_32_BIT)"; 287 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 288 | GCC_C_LANGUAGE_STANDARD = c99; 289 | GCC_WARN_ABOUT_RETURN_TYPE = YES; 290 | GCC_WARN_UNUSED_VARIABLE = YES; 291 | IPHONEOS_DEPLOYMENT_TARGET = 3.1; 292 | OTHER_CFLAGS = "-DNS_BLOCK_ASSERTIONS=1"; 293 | PREBINDING = NO; 294 | SDKROOT = iphoneos4.0; 295 | }; 296 | name = Release; 297 | }; 298 | /* End XCBuildConfiguration section */ 299 | 300 | /* Begin XCConfigurationList section */ 301 | 1D6058960D05DD3E006BFB54 /* Build configuration list for PBXNativeTarget "MultiThreadedCoreData" */ = { 302 | isa = XCConfigurationList; 303 | buildConfigurations = ( 304 | 1D6058940D05DD3E006BFB54 /* Debug */, 305 | 1D6058950D05DD3E006BFB54 /* Release */, 306 | ); 307 | defaultConfigurationIsVisible = 0; 308 | defaultConfigurationName = Release; 309 | }; 310 | C01FCF4E08A954540054247B /* Build configuration list for PBXProject "MultiThreadedCoreData" */ = { 311 | isa = XCConfigurationList; 312 | buildConfigurations = ( 313 | C01FCF4F08A954540054247B /* Debug */, 314 | C01FCF5008A954540054247B /* Release */, 315 | ); 316 | defaultConfigurationIsVisible = 0; 317 | defaultConfigurationName = Release; 318 | }; 319 | /* End XCConfigurationList section */ 320 | 321 | /* Begin XCVersionGroup section */ 322 | 28D3F201112F7DC200FD0661 /* MultiThreadedCoreData.xcdatamodeld */ = { 323 | isa = XCVersionGroup; 324 | children = ( 325 | 28860B760F44E54D00985440 /* MultiThreadedCoreData.xcdatamodel */, 326 | ); 327 | currentVersion = 28860B760F44E54D00985440 /* MultiThreadedCoreData.xcdatamodel */; 328 | path = MultiThreadedCoreData.xcdatamodeld; 329 | sourceTree = ""; 330 | versionGroupType = wrapper.xcdatamodel; 331 | }; 332 | /* End XCVersionGroup section */ 333 | }; 334 | rootObject = 29B97313FDCFA39411CA2CEA /* Project object */; 335 | } 336 | -------------------------------------------------------------------------------- /Classes/RootViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // RootViewController.m 3 | // MultiThreadedCoreData 4 | // 5 | // Created by James Abley on 04/07/2010. 6 | // Copyright Mobile IQ Ltd 2010. All rights reserved. 7 | // 8 | 9 | #import "RootViewController.h" 10 | #import "Person.h" 11 | #import "UpdateFirstNameOperation.h" 12 | #import "UpdateSurnameOperation.h" 13 | #import "MergePolicySelectionEditor.h" 14 | 15 | enum TableSections { 16 | TableSectionPersons, 17 | TableSectionButtons, 18 | TableSectionMergePolicies 19 | }; 20 | 21 | @interface RootViewController () 22 | 23 | - (void)configureCell:(UITableViewCell *)cell atIndexPath:(NSIndexPath *)indexPath; 24 | 25 | /** 26 | Returns a string representation of the merge policy. 27 | */ 28 | - (NSString*)mergePolicyName:(id)mergePolicy; 29 | 30 | /** 31 | Resets the merge polices 32 | */ 33 | - (void)resetWasTapped:(id)sender; 34 | 35 | /** 36 | Resets the Person to the initial values. 37 | */ 38 | - (void)resetPerson:(Person*)person; 39 | 40 | /** 41 | Runs the long-running background operations which will update Core Data. 42 | */ 43 | - (void)runWasTapped:(id)sender; 44 | 45 | @end 46 | 47 | 48 | @implementation RootViewController 49 | 50 | @synthesize fetchedResultsController; 51 | @synthesize managedObjectContext; 52 | @synthesize mainMergePolicy; 53 | @synthesize threadedMergePolicy; 54 | 55 | #pragma mark - 56 | #pragma mark View lifecycle 57 | 58 | - (void)viewDidLoad { 59 | [super viewDidLoad]; 60 | 61 | [self setTitle:@"Core Data threading"]; 62 | 63 | NSError *error = nil; 64 | if (![[self fetchedResultsController] performFetch:&error]) { 65 | /* 66 | Replace this implementation with code to handle the error appropriately. 67 | 68 | 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. If it is not possible to recover from the error, display an alert panel that instructs the user to quit the application by pressing the Home button. 69 | */ 70 | NSLog(@"Unresolved error %@, %@", error, [error userInfo]); 71 | abort(); 72 | } 73 | 74 | if ([[[self fetchedResultsController] fetchedObjects] count] == 0) { 75 | Person *person = (Person*)[NSEntityDescription insertNewObjectForEntityForName:@"Person" 76 | inManagedObjectContext:[self managedObjectContext]]; 77 | [self resetPerson:person]; 78 | } 79 | 80 | taskQueue_ = [[NSOperationQueue alloc] init]; 81 | [taskQueue_ setMaxConcurrentOperationCount:2]; 82 | 83 | [self resetWasTapped:nil]; 84 | } 85 | 86 | - (void)viewDidAppear:(BOOL)animated { 87 | [super viewDidAppear:animated]; 88 | [[self tableView] reloadData]; 89 | } 90 | 91 | #pragma mark - 92 | #pragma mark Extension methods 93 | - (void)configureCell:(UITableViewCell *)cell atIndexPath:(NSIndexPath *)indexPath { 94 | 95 | NSManagedObject *managedObject = [fetchedResultsController objectAtIndexPath:indexPath]; 96 | cell.textLabel.text = [[managedObject valueForKey:@"firstName"] description]; 97 | [[cell detailTextLabel] setText:[[managedObject valueForKey:@"surname"] description]]; 98 | } 99 | 100 | - (NSString*)mergePolicyName:(id)mergePolicy { 101 | if (mergePolicy == NSErrorMergePolicy) { 102 | return @"NSErrorMergePolicy"; 103 | } else if (mergePolicy == NSMergeByPropertyObjectTrumpMergePolicy) { 104 | return @"NSMergeByPropertyObjectTrumpMergePolicy"; 105 | } else if (mergePolicy == NSMergeByPropertyStoreTrumpMergePolicy) { 106 | return @"NSMergeByPropertyStoreTrumpMergePolicy"; 107 | } else if (mergePolicy == NSOverwriteMergePolicy) { 108 | return @"NSOverwriteMergePolicy"; 109 | } else if (mergePolicy == NSRollbackMergePolicy) { 110 | return @"NSRollbackMergePolicy"; 111 | } else { 112 | return [mergePolicy description]; 113 | } 114 | } 115 | 116 | - (void)resetWasTapped:(id)sender { 117 | mainMergePolicy = NSErrorMergePolicy; 118 | threadedMergePolicy = NSErrorMergePolicy; 119 | Person *person = [[[self fetchedResultsController] fetchedObjects] objectAtIndex:0]; 120 | [self resetPerson:person]; 121 | [[self tableView] reloadData]; 122 | } 123 | 124 | - (void)resetPerson:(Person*)person { 125 | [person setFirstName:@"George"]; 126 | [person setSurname:@"Washington"]; 127 | [[self managedObjectContext] save:NULL]; 128 | } 129 | 130 | - (void)runWasTapped:(id)sender { 131 | NSManagedObjectID *entityID = [[[[self fetchedResultsController] fetchedObjects] objectAtIndex:0] objectID]; 132 | [[self managedObjectContext] setMergePolicy:mainMergePolicy]; 133 | 134 | NSOperation *firstName = [[UpdateFirstNameOperation alloc] initWithManagedObjectContext:[self managedObjectContext] 135 | mergePolicy:threadedMergePolicy 136 | entityID:entityID]; 137 | NSOperation *surname = [[UpdateSurnameOperation alloc] initWithManagedObjectContext:[self managedObjectContext] 138 | mergePolicy:threadedMergePolicy 139 | entityID:entityID]; 140 | 141 | [taskQueue_ addOperation:firstName]; 142 | [taskQueue_ addOperation:surname]; 143 | } 144 | 145 | #pragma mark - 146 | #pragma mark Table view data source 147 | 148 | - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView { 149 | /* 150 | First section - Person. 151 | Second section - Operation buttons. 152 | Third section - select merge policies. 153 | */ 154 | return 3; 155 | } 156 | 157 | 158 | - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { 159 | switch (section) { 160 | case TableSectionPersons: { 161 | id sectionInfo = [[fetchedResultsController sections] objectAtIndex:section]; 162 | return [sectionInfo numberOfObjects]; 163 | } 164 | case TableSectionButtons: { 165 | return 1; 166 | } 167 | case TableSectionMergePolicies: { 168 | return 2; 169 | } 170 | default: { 171 | [NSException raise:NSInvalidArgumentException format:@"No such section"]; 172 | return -1; 173 | } 174 | } 175 | } 176 | 177 | 178 | // Customize the appearance of table view cells. 179 | - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { 180 | 181 | switch ([indexPath section]) { 182 | case TableSectionPersons: { 183 | static NSString *CellIdentifier = @"Cell"; 184 | 185 | UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier]; 186 | if (cell == nil) { 187 | cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier] autorelease]; 188 | } 189 | 190 | // Configure the cell. 191 | Person *person = (Person*) [fetchedResultsController objectAtIndexPath:indexPath]; 192 | [[cell textLabel] setText:[person firstName]]; 193 | [[cell detailTextLabel] setText:[person surname]]; 194 | 195 | return cell; 196 | } case TableSectionButtons: { 197 | static NSString *CellIdentifier = @"ButtonCell"; 198 | 199 | UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier]; 200 | if (cell == nil) { 201 | cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease]; 202 | } 203 | 204 | // Add a reset and run button. 205 | UIButton *reset = [UIButton buttonWithType:UIButtonTypeRoundedRect]; 206 | [reset addTarget:self action:@selector(resetWasTapped:) forControlEvents:UIControlEventTouchUpInside]; 207 | [reset setFrame:CGRectMake(7, 7, 100, 30)]; 208 | [reset setTitle:@"Reset" forState:UIControlStateNormal]; 209 | [cell addSubview:reset]; 210 | 211 | UIButton *run = [UIButton buttonWithType:UIButtonTypeRoundedRect]; 212 | [run addTarget:self action:@selector(runWasTapped:) forControlEvents:UIControlEventTouchUpInside]; 213 | [run setFrame:CGRectMake(200, 7, 100, 30)]; 214 | [run setTitle:@"Run" forState:UIControlStateNormal]; 215 | [cell addSubview:run]; 216 | 217 | return cell; 218 | } 219 | case TableSectionMergePolicies: { 220 | 221 | static NSString *CellIdentifier = @"MergePolicyCell"; 222 | 223 | UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier]; 224 | if (cell == nil) { 225 | cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease]; 226 | } 227 | 228 | // Configure the cell. 229 | [cell setAccessoryType:UITableViewCellAccessoryDisclosureIndicator]; 230 | 231 | switch ([indexPath row]) { 232 | case 0: { 233 | [[cell textLabel] setText:[NSString stringWithFormat:@"Main (%@)", [self mergePolicyName:mainMergePolicy]]]; 234 | break; 235 | } 236 | case 1: { 237 | [[cell textLabel] setText:[NSString stringWithFormat:@"BG (%@)", [self mergePolicyName:threadedMergePolicy]]]; 238 | break; 239 | } 240 | default: 241 | break; 242 | } 243 | 244 | return cell; 245 | } 246 | default: { 247 | [NSException raise:NSInvalidArgumentException format:@"No such section"]; 248 | return nil; 249 | } 250 | } 251 | } 252 | 253 | 254 | 255 | /* 256 | // Override to support conditional editing of the table view. 257 | - (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath { 258 | // Return NO if you do not want the specified item to be editable. 259 | return YES; 260 | } 261 | */ 262 | 263 | 264 | // Override to support editing the table view. 265 | - (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath { 266 | 267 | if (editingStyle == UITableViewCellEditingStyleDelete) { 268 | // Delete the managed object for the given index path 269 | NSManagedObjectContext *context = [fetchedResultsController managedObjectContext]; 270 | [context deleteObject:[fetchedResultsController objectAtIndexPath:indexPath]]; 271 | 272 | // Save the context. 273 | NSError *error = nil; 274 | if (![context save:&error]) { 275 | /* 276 | Replace this implementation with code to handle the error appropriately. 277 | 278 | 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. If it is not possible to recover from the error, display an alert panel that instructs the user to quit the application by pressing the Home button. 279 | */ 280 | NSLog(@"Unresolved error %@, %@", error, [error userInfo]); 281 | abort(); 282 | } 283 | } 284 | } 285 | 286 | 287 | - (BOOL)tableView:(UITableView *)tableView canMoveRowAtIndexPath:(NSIndexPath *)indexPath { 288 | // The table view should not be re-orderable. 289 | return NO; 290 | } 291 | 292 | 293 | #pragma mark - 294 | #pragma mark Table view delegate 295 | 296 | - (NSIndexPath *) tableView:(UITableView *)tableView willSelectRowAtIndexPath:(NSIndexPath *)indexPath { 297 | switch ([indexPath section]) { 298 | case TableSectionPersons: // Don't allow selection of the Person rows 299 | case TableSectionButtons: // Don't allow selection of the buttons either 300 | return nil; 301 | default: 302 | return indexPath; 303 | } 304 | } 305 | 306 | - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { 307 | /* Push the view controller responsible for selecting a merge policy. */ 308 | MergePolicySelectionEditor *mergePolicySelectionEditor = [[MergePolicySelectionEditor alloc] init]; 309 | [mergePolicySelectionEditor setTarget:self]; 310 | [mergePolicySelectionEditor setMergePoliciesList:[NSArray arrayWithObjects:NSErrorMergePolicy, 311 | NSMergeByPropertyStoreTrumpMergePolicy, 312 | NSMergeByPropertyObjectTrumpMergePolicy, 313 | NSOverwriteMergePolicy, 314 | NSRollbackMergePolicy, 315 | nil]]; 316 | 317 | switch (indexPath.row) { 318 | case 0: { 319 | [mergePolicySelectionEditor setKeypath:@"mainMergePolicy"]; 320 | break; 321 | } 322 | case 1: { 323 | [mergePolicySelectionEditor setKeypath:@"threadedMergePolicy"]; 324 | break; 325 | } 326 | default: 327 | break; 328 | } 329 | 330 | [[self navigationController] pushViewController:mergePolicySelectionEditor animated:YES]; 331 | [mergePolicySelectionEditor release]; 332 | 333 | [tableView deselectRowAtIndexPath:indexPath animated:YES]; 334 | } 335 | 336 | 337 | #pragma mark - 338 | #pragma mark Fetched results controller 339 | 340 | - (NSFetchedResultsController *)fetchedResultsController { 341 | 342 | if (fetchedResultsController != nil) { 343 | return fetchedResultsController; 344 | } 345 | 346 | /* 347 | Set up the fetched results controller. 348 | */ 349 | // Create the fetch request for the entity. 350 | NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init]; 351 | // Edit the entity name as appropriate. 352 | NSEntityDescription *entity = [NSEntityDescription entityForName:@"Person" inManagedObjectContext:managedObjectContext]; 353 | [fetchRequest setEntity:entity]; 354 | 355 | // Set the batch size to a suitable number. 356 | [fetchRequest setFetchBatchSize:20]; 357 | 358 | // Edit the sort key as appropriate. 359 | NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"surname" ascending:NO]; 360 | NSArray *sortDescriptors = [[NSArray alloc] initWithObjects:sortDescriptor, nil]; 361 | 362 | [fetchRequest setSortDescriptors:sortDescriptors]; 363 | 364 | // Edit the section name key path and cache name if appropriate. 365 | // nil for section name key path means "no sections". 366 | NSFetchedResultsController *aFetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest 367 | managedObjectContext:managedObjectContext 368 | sectionNameKeyPath:nil 369 | cacheName:@"Root"]; 370 | [aFetchedResultsController setDelegate:self]; 371 | 372 | [self setFetchedResultsController:aFetchedResultsController]; 373 | [aFetchedResultsController release]; 374 | [fetchRequest release]; 375 | 376 | [sortDescriptor release]; 377 | [sortDescriptors release]; 378 | 379 | return fetchedResultsController; 380 | } 381 | 382 | 383 | #pragma mark - 384 | #pragma mark Fetched results controller delegate 385 | 386 | 387 | - (void)controllerWillChangeContent:(NSFetchedResultsController *)controller { 388 | [self.tableView beginUpdates]; 389 | } 390 | 391 | 392 | - (void)controller:(NSFetchedResultsController *)controller didChangeSection:(id )sectionInfo 393 | atIndex:(NSUInteger)sectionIndex forChangeType:(NSFetchedResultsChangeType)type { 394 | 395 | switch(type) { 396 | case NSFetchedResultsChangeInsert: 397 | [self.tableView insertSections:[NSIndexSet indexSetWithIndex:sectionIndex] withRowAnimation:UITableViewRowAnimationFade]; 398 | break; 399 | 400 | case NSFetchedResultsChangeDelete: 401 | [self.tableView deleteSections:[NSIndexSet indexSetWithIndex:sectionIndex] withRowAnimation:UITableViewRowAnimationFade]; 402 | break; 403 | } 404 | } 405 | 406 | 407 | - (void)controller:(NSFetchedResultsController *)controller didChangeObject:(id)anObject 408 | atIndexPath:(NSIndexPath *)indexPath forChangeType:(NSFetchedResultsChangeType)type 409 | newIndexPath:(NSIndexPath *)newIndexPath { 410 | 411 | UITableView *tableView = self.tableView; 412 | 413 | switch(type) { 414 | 415 | case NSFetchedResultsChangeInsert: 416 | [tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath] withRowAnimation:UITableViewRowAnimationFade]; 417 | break; 418 | 419 | case NSFetchedResultsChangeDelete: 420 | [tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade]; 421 | break; 422 | 423 | case NSFetchedResultsChangeUpdate: 424 | [self configureCell:[tableView cellForRowAtIndexPath:indexPath] atIndexPath:indexPath]; 425 | break; 426 | 427 | case NSFetchedResultsChangeMove: 428 | [tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade]; 429 | [tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath]withRowAnimation:UITableViewRowAnimationFade]; 430 | break; 431 | } 432 | } 433 | 434 | 435 | - (void)controllerDidChangeContent:(NSFetchedResultsController *)controller { 436 | [self.tableView endUpdates]; 437 | } 438 | 439 | 440 | /* 441 | // Implementing the above methods to update the table view in response to individual changes may have performance implications if a large number of changes are made simultaneously. If this proves to be an issue, you can instead just implement controllerDidChangeContent: which notifies the delegate that all section and object changes have been processed. 442 | 443 | - (void)controllerDidChangeContent:(NSFetchedResultsController *)controller { 444 | // In the simplest, most efficient, case, reload the table view. 445 | [self.tableView reloadData]; 446 | } 447 | */ 448 | 449 | 450 | #pragma mark - 451 | #pragma mark Memory management 452 | 453 | - (void)didReceiveMemoryWarning { 454 | // Releases the view if it doesn't have a superview. 455 | [super didReceiveMemoryWarning]; 456 | 457 | // Relinquish ownership any cached data, images, etc that aren't in use. 458 | } 459 | 460 | 461 | - (void)viewDidUnload { 462 | // Relinquish ownership of anything that can be recreated in viewDidLoad or on demand. 463 | // For example: self.myOutlet = nil; 464 | } 465 | 466 | 467 | - (void)dealloc { 468 | [fetchedResultsController release]; 469 | [managedObjectContext release]; 470 | [taskQueue_ release]; 471 | 472 | [super dealloc]; 473 | } 474 | 475 | @end 476 | -------------------------------------------------------------------------------- /RootViewController.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 784 5 | 10F569 6 | 762 7 | 1038.29 8 | 461.00 9 | 10 | com.apple.InterfaceBuilder.IBCocoaTouchPlugin 11 | 87 12 | 13 | 14 | YES 15 | 16 | 17 | 18 | YES 19 | com.apple.InterfaceBuilder.IBCocoaTouchPlugin 20 | 21 | 22 | YES 23 | 24 | YES 25 | 26 | 27 | YES 28 | 29 | 30 | 31 | YES 32 | 33 | IBFilesOwner 34 | IBCocoaTouchFramework 35 | 36 | 37 | IBFirstResponder 38 | IBCocoaTouchFramework 39 | 40 | 41 | 42 | 274 43 | {320, 247} 44 | 45 | 46 | 3 47 | MQA 48 | 49 | NO 50 | YES 51 | NO 52 | IBCocoaTouchFramework 53 | NO 54 | 1 55 | 0 56 | YES 57 | 44 58 | 22 59 | 22 60 | 61 | 62 | 63 | 64 | YES 65 | 66 | 67 | view 68 | 69 | 70 | 71 | 3 72 | 73 | 74 | 75 | dataSource 76 | 77 | 78 | 79 | 4 80 | 81 | 82 | 83 | delegate 84 | 85 | 86 | 87 | 5 88 | 89 | 90 | 91 | 92 | YES 93 | 94 | 0 95 | 96 | 97 | 98 | 99 | 100 | -1 101 | 102 | 103 | File's Owner 104 | 105 | 106 | -2 107 | 108 | 109 | 110 | 111 | 2 112 | 113 | 114 | YES 115 | 116 | 117 | 118 | 119 | 120 | 121 | YES 122 | 123 | YES 124 | -1.CustomClassName 125 | -2.CustomClassName 126 | 2.IBEditorWindowLastContentRect 127 | 2.IBPluginDependency 128 | 129 | 130 | YES 131 | RootViewController 132 | UIResponder 133 | {{329, 609}, {320, 247}} 134 | com.apple.InterfaceBuilder.IBCocoaTouchPlugin 135 | 136 | 137 | 138 | YES 139 | 140 | 141 | YES 142 | 143 | 144 | 145 | 146 | YES 147 | 148 | 149 | YES 150 | 151 | 152 | 153 | 9 154 | 155 | 156 | 157 | YES 158 | 159 | RootViewController 160 | UITableViewController 161 | 162 | YES 163 | 164 | YES 165 | mainMergePolicy 166 | threadedMergePolicy 167 | 168 | 169 | YES 170 | id 171 | id 172 | 173 | 174 | 175 | IBProjectSource 176 | Classes/RootViewController.h 177 | 178 | 179 | 180 | 181 | YES 182 | 183 | NSObject 184 | 185 | IBFrameworkSource 186 | Foundation.framework/Headers/NSError.h 187 | 188 | 189 | 190 | NSObject 191 | 192 | IBFrameworkSource 193 | Foundation.framework/Headers/NSFileManager.h 194 | 195 | 196 | 197 | NSObject 198 | 199 | IBFrameworkSource 200 | Foundation.framework/Headers/NSKeyValueCoding.h 201 | 202 | 203 | 204 | NSObject 205 | 206 | IBFrameworkSource 207 | Foundation.framework/Headers/NSKeyValueObserving.h 208 | 209 | 210 | 211 | NSObject 212 | 213 | IBFrameworkSource 214 | Foundation.framework/Headers/NSKeyedArchiver.h 215 | 216 | 217 | 218 | NSObject 219 | 220 | IBFrameworkSource 221 | Foundation.framework/Headers/NSNetServices.h 222 | 223 | 224 | 225 | NSObject 226 | 227 | IBFrameworkSource 228 | Foundation.framework/Headers/NSObject.h 229 | 230 | 231 | 232 | NSObject 233 | 234 | IBFrameworkSource 235 | Foundation.framework/Headers/NSPort.h 236 | 237 | 238 | 239 | NSObject 240 | 241 | IBFrameworkSource 242 | Foundation.framework/Headers/NSRunLoop.h 243 | 244 | 245 | 246 | NSObject 247 | 248 | IBFrameworkSource 249 | Foundation.framework/Headers/NSStream.h 250 | 251 | 252 | 253 | NSObject 254 | 255 | IBFrameworkSource 256 | Foundation.framework/Headers/NSThread.h 257 | 258 | 259 | 260 | NSObject 261 | 262 | IBFrameworkSource 263 | Foundation.framework/Headers/NSURL.h 264 | 265 | 266 | 267 | NSObject 268 | 269 | IBFrameworkSource 270 | Foundation.framework/Headers/NSURLConnection.h 271 | 272 | 273 | 274 | NSObject 275 | 276 | IBFrameworkSource 277 | Foundation.framework/Headers/NSXMLParser.h 278 | 279 | 280 | 281 | NSObject 282 | 283 | IBFrameworkSource 284 | UIKit.framework/Headers/UIAccessibility.h 285 | 286 | 287 | 288 | NSObject 289 | 290 | IBFrameworkSource 291 | UIKit.framework/Headers/UINibLoading.h 292 | 293 | 294 | 295 | NSObject 296 | 297 | IBFrameworkSource 298 | UIKit.framework/Headers/UIResponder.h 299 | 300 | 301 | 302 | UIResponder 303 | NSObject 304 | 305 | 306 | 307 | UIScrollView 308 | UIView 309 | 310 | IBFrameworkSource 311 | UIKit.framework/Headers/UIScrollView.h 312 | 313 | 314 | 315 | UISearchBar 316 | UIView 317 | 318 | IBFrameworkSource 319 | UIKit.framework/Headers/UISearchBar.h 320 | 321 | 322 | 323 | UISearchDisplayController 324 | NSObject 325 | 326 | IBFrameworkSource 327 | UIKit.framework/Headers/UISearchDisplayController.h 328 | 329 | 330 | 331 | UITableView 332 | UIScrollView 333 | 334 | IBFrameworkSource 335 | UIKit.framework/Headers/UITableView.h 336 | 337 | 338 | 339 | UITableViewController 340 | UIViewController 341 | 342 | IBFrameworkSource 343 | UIKit.framework/Headers/UITableViewController.h 344 | 345 | 346 | 347 | UIView 348 | 349 | IBFrameworkSource 350 | UIKit.framework/Headers/UITextField.h 351 | 352 | 353 | 354 | UIView 355 | UIResponder 356 | 357 | IBFrameworkSource 358 | UIKit.framework/Headers/UIView.h 359 | 360 | 361 | 362 | UIViewController 363 | 364 | IBFrameworkSource 365 | UIKit.framework/Headers/UINavigationController.h 366 | 367 | 368 | 369 | UIViewController 370 | 371 | IBFrameworkSource 372 | UIKit.framework/Headers/UITabBarController.h 373 | 374 | 375 | 376 | UIViewController 377 | UIResponder 378 | 379 | IBFrameworkSource 380 | UIKit.framework/Headers/UIViewController.h 381 | 382 | 383 | 384 | 385 | 0 386 | IBCocoaTouchFramework 387 | 388 | com.apple.InterfaceBuilder.CocoaTouchPlugin.iPhoneOS 389 | 390 | 391 | 392 | com.apple.InterfaceBuilder.CocoaTouchPlugin.InterfaceBuilder3 393 | 394 | 395 | YES 396 | MultiThreadedCoreData.xcodeproj 397 | 3 398 | 87 399 | 400 | 401 | --------------------------------------------------------------------------------