Add workaround to avoid potential crash during execution of deletion operations on iOS 5. closes #1266

This commit is contained in:
Blake Watters
2013-03-13 22:28:24 -04:00
parent 58d67c2229
commit e831e03ccb
2 changed files with 38 additions and 2 deletions

View File

@@ -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);
}];
}];

View File

@@ -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