diff --git a/Code/CoreData/RKManagedObjectMappingOperationDataSource.m b/Code/CoreData/RKManagedObjectMappingOperationDataSource.m index d21069a5..ff2daab5 100644 --- a/Code/CoreData/RKManagedObjectMappingOperationDataSource.m +++ b/Code/CoreData/RKManagedObjectMappingOperationDataSource.m @@ -326,10 +326,15 @@ extern NSString * const RKObjectMappingNestingAttributeKeyName; // Delete the object immediately if there are no connections that may make it valid if ([connections count] == 0 && RKDeleteInvalidNewManagedObject(mappingOperation.destinationObject)) return YES; - // Attempt to establish the connections and delete the object if its invalid once we are done + /** + Attempt to establish the connections and delete the object if its invalid once we are done + + NOTE: We obtain a weak reference to the MOC to avoid a potential crash under iOS 5 if the MOC is deallocated before the operation executes. Under iOS 6, the object returns a nil `managedObjectContext` and the `performBlockAndWait:` message is sent to nil. + */ NSOperationQueue *operationQueue = self.operationQueue ?: [NSOperationQueue currentQueue]; + __weak NSManagedObjectContext *weakContext = [(NSManagedObject *)mappingOperation.destinationObject managedObjectContext]; NSBlockOperation *deletionOperation = [NSBlockOperation blockOperationWithBlock:^{ - [[(NSManagedObject *)mappingOperation.destinationObject managedObjectContext] performBlockAndWait:^{ + [weakContext performBlockAndWait:^{ RKDeleteInvalidNewManagedObject(mappingOperation.destinationObject); }]; }]; diff --git a/Tests/Logic/CoreData/RKManagedObjectMappingOperationDataSourceTest.m b/Tests/Logic/CoreData/RKManagedObjectMappingOperationDataSourceTest.m index 69df82bc..7c86d5d3 100644 --- a/Tests/Logic/CoreData/RKManagedObjectMappingOperationDataSourceTest.m +++ b/Tests/Logic/CoreData/RKManagedObjectMappingOperationDataSourceTest.m @@ -1363,4 +1363,35 @@ expect([blake valueForKey:@"favoriteCat"]).to.equal([mapper.mappingResult.dictionary objectForKey:@"cat"]); } +- (void)testDeletionOperationAfterManagedObjectContextIsDeallocated +{ + RKManagedObjectStore *managedObjectStore = [RKTestFactory managedObjectStore]; + NSManagedObjectContext *managedObjectContext = [managedObjectStore newChildManagedObjectContextWithConcurrencyType:NSPrivateQueueConcurrencyType]; + RKManagedObjectMappingOperationDataSource *dataSource = [[RKManagedObjectMappingOperationDataSource alloc] initWithManagedObjectContext:managedObjectContext cache:nil]; + + + NSDictionary *representation = @{ @"human": @{ @"name": @"Blake Watters", @"favoriteCatID": @(12345) }, @"cat": @{ @"railsID": @(12345) } }; + RKEntityMapping *catMapping = [RKEntityMapping mappingForEntityForName:@"Cat" + inManagedObjectStore:managedObjectStore]; + RKCat *cat = [NSEntityDescription insertNewObjectForEntityForName:@"Cat" inManagedObjectContext:managedObjectContext]; + RKMappingOperation *mappingOperation = [[RKMappingOperation alloc] initWithSourceObject:representation destinationObject:cat mapping:catMapping]; + NSOperationQueue *operationQueue = [NSOperationQueue new]; + [operationQueue setSuspended:YES]; + dataSource.operationQueue = operationQueue; + + id mockOperation = [OCMockObject partialMockForObject:mappingOperation]; + [[[mockOperation stub] andReturn:catMapping] objectMapping]; + [dataSource commitChangesForMappingOperation:mockOperation error:nil]; + + expect([operationQueue operationCount]).to.equal(1); + dataSource = nil; + managedObjectContext = nil; + [operationQueue setSuspended:NO]; + + [operationQueue waitUntilAllOperationsAreFinished]; + // Create a operation queue + // Create data source + +} + @end