├── .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 |
13 |
17 |
21 |
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 |
--------------------------------------------------------------------------------