Commits

dmakarenko committed 41928d0

Initial version commit.

  • Participants

Comments (0)

Files changed (2)

File CDOperation.h

+//
+//  CDOperation.h
+//
+//  Created by Dmitry Makarenko on 05.06.11.
+//  Copyright 2011 Dmitry Makarenko. All rights reserved.
+//
+
+#import <Foundation/Foundation.h>
+#import <CoreData/CoreData.h>
+
+@interface CDOperation : NSOperation {
+    NSThread* initialThread_;
+    NSManagedObjectContext* mainManagedObjectContext_;
+    NSManagedObjectContext* operationManagedObjectContext_;
+    NSMutableArray* changeNotifications_;
+}
+
+- (id) initWithMainManagedObjectContext:(NSManagedObjectContext*)context;
+
+@end

File CDOperation.m

+//
+//  CDOperation.m
+//
+//  Created by Dmitry Makarenko on 05.06.11.
+//  Copyright 2011 Dmitry Makarenko. All rights reserved.
+//
+
+#import "CDOperation.h"
+
+@interface NSManagedObjectContext (Additions)
+
+- (BOOL) save;
+
+@end
+
+@implementation NSManagedObjectContext (Additions)
+
+- (BOOL) save {
+    NSError *error = nil;
+    BOOL status = YES;
+    if ([self hasChanges]) {
+        if (![self save:&error]) {
+            NSLog(@"Unresolved context save error: %@, %@", error, [error userInfo]);
+            status = NO;
+        }
+    }
+    return status;
+}
+
+@end
+
+
+@interface CDOperation ()
+
+@property (nonatomic, assign) NSThread* initialThread;
+@property (nonatomic, retain) NSManagedObjectContext* mainManagedObjectContext;
+@property (nonatomic, retain) NSManagedObjectContext* operationManagedObjectContext;
+
+@property (nonatomic, retain) NSMutableArray* changeNotifications;
+
+- (void) processMainContextChangeNotification:(NSNotification*)notification;
+- (void) mergeMainContextChangesIntoOperationContext;
+
+- (void) processOperationContextChangeNotification:(NSNotification*)notification;
+- (void) mergeOperationContextChangesIntoMainContext:(NSNotification*)notification;
+
+- (void) doMain;
+
+@end
+
+@implementation CDOperation
+
+@synthesize initialThread = initialThread_;
+@synthesize mainManagedObjectContext = mainManagedObjectContext_;
+@synthesize operationManagedObjectContext = operationManagedObjectContext_;
+@synthesize changeNotifications = changeNotifications_;
+
+- (void) processMainContextChangeNotification:(NSNotification*)notification {
+    @synchronized(self) {
+        [self.changeNotifications addObject:notification];
+    }
+}
+
+- (void) mergeMainContextChangesIntoOperationContext {
+    @synchronized(self) {
+        if ([changeNotifications_ count] > 0) {
+            NSLog(@"Merging changes from MAIN context to OPERATION context started");
+            for(NSNotification* change in self.changeNotifications) {
+                [operationManagedObjectContext_ mergeChangesFromContextDidSaveNotification:change];
+            }
+            [operationManagedObjectContext_ save];
+            [self.changeNotifications removeAllObjects];
+            NSLog(@"Merging changes from MAIN context to OPERATION context finished");
+        }
+    }
+}
+
+- (void) processOperationContextChangeNotification:(NSNotification*)notification {
+    [self performSelector:@selector(mergeOperationContextChangesIntoMainContext:)
+                 onThread:self.initialThread
+               withObject:notification
+            waitUntilDone:NO];
+}
+
+- (void) mergeOperationContextChangesIntoMainContext:(NSNotification*)notification {
+    NSLog(@"Merging changes from OPERATION context to MAIN context started");
+    [mainManagedObjectContext_ mergeChangesFromContextDidSaveNotification:notification];
+    [mainManagedObjectContext_ save];
+    NSLog(@"Merging changes from OPERATION context to MAIN context finished");
+}
+
+- (id) initWithMainManagedObjectContext:(NSManagedObjectContext*)context {
+    if ((self = [super init])) {
+        self.initialThread = [NSThread currentThread];
+        self.mainManagedObjectContext = context;
+        self.operationManagedObjectContext = nil;
+        self.changeNotifications = [NSMutableArray arrayWithCapacity:4];
+
+        // Observing main thread's (or thread's, where that operation was created) NSMangedObjectContext save notification.
+        [[NSNotificationCenter defaultCenter] addObserver:self
+                                                 selector:@selector(processMainContextChangeNotification:)
+                                                     name:NSManagedObjectContextDidSaveNotification
+                                                   object:mainManagedObjectContext_];
+ 
+    }
+    return self;
+}
+
+- (void) main {
+    NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
+    @try {
+        if (![self isCancelled]) {
+
+            self.operationManagedObjectContext = [[[NSManagedObjectContext alloc] init] autorelease];
+            [operationManagedObjectContext_ setPersistentStoreCoordinator:[mainManagedObjectContext_ persistentStoreCoordinator]];
+
+             // Observing operation's thread NSMangedObjectContext save notification.
+            [[NSNotificationCenter defaultCenter] addObserver:self
+                                                     selector:@selector(processOperationContextChangeNotification:)
+                                                         name:NSManagedObjectContextDidSaveNotification
+                                                       object:operationManagedObjectContext_];
+            
+            [self doMain];
+            
+            [self mergeMainContextChangesIntoOperationContext];
+        }
+    }
+    @catch(NSException* exception) {
+        NSLog(@"Exception: %@, %@", exception.name, exception.reason);
+    }
+    @catch(...) {
+        NSLog(@"%@", @"Not NSException-based exception!");
+    }
+    @finally {
+        [[NSNotificationCenter defaultCenter] removeObserver:self];
+        self.operationManagedObjectContext = nil;
+        [pool release];
+    }
+}
+
+- (void) doMain {
+    NSAssert(NO, @"Method has to be overriden in subclasses!");
+}
+
+#pragma mark Memory management
+
+- (void) dealloc {
+    self.initialThread = nil;
+    self.mainManagedObjectContext = nil;
+    self.changeNotifications = nil;
+    
+    [super dealloc];
+}
+
+@end