mirror of
https://github.com/zhigang1992/RestKit.git
synced 2026-04-23 20:31:13 +08:00
Add more cancellation backstopping to avoid NSObjectInaccessibleException crashes when executing RKManagedObjectRequestOperation instances that have been cancelled and the Core Data environment has shifted underneath them (i.e. records have been deleted from the store)
This commit is contained in:
@@ -185,6 +185,8 @@ static NSDictionary *RKConnectionAttributeValuesWithObject(RKConnectionDescripti
|
||||
NSString *relationshipName = self.connection.relationship.name;
|
||||
RKLogTrace(@"Connecting relationship '%@' with mapping: %@", relationshipName, self.connection);
|
||||
[self.managedObjectContext performBlockAndWait:^{
|
||||
if (self.isCancelled || [self.managedObject isDeleted]) return;
|
||||
|
||||
BOOL shouldConnect = YES;
|
||||
self.connectedValue = [self findConnected:&shouldConnect];
|
||||
if (shouldConnect) {
|
||||
|
||||
@@ -501,12 +501,45 @@ static NSURL *RKRelativeURLFromURLAndResponseDescriptors(NSURL *URL, NSArray *re
|
||||
return YES;
|
||||
}
|
||||
|
||||
/**
|
||||
NOTE: This is more or less a direct port of the functionality provided by `[NSManagedObjectContext saveToPersistentStore:]` in the `RKAdditions` category. We have duplicated the logic here to add in support for checking if the operation has been cancelled since we began cascading up the MOC chain. Because each `performBlockAndWait:` invocation essentially jumps threads and is subject to the availability of the context, it is very possible for the operation to be cancelled during this part of the operation's lifecycle.
|
||||
*/
|
||||
- (BOOL)saveContextToPersistentStore:(NSManagedObjectContext *)contextToSave error:(NSError **)error
|
||||
{
|
||||
__block NSError *localError = nil;
|
||||
while (contextToSave) {
|
||||
__block BOOL success;
|
||||
[contextToSave performBlockAndWait:^{
|
||||
if (! [self isCancelled]) {
|
||||
success = [contextToSave save:&localError];
|
||||
if (! success && localError == nil) RKLogWarning(@"Saving of managed object context failed, but a `nil` value for the `error` argument was returned. This typically indicates an invalid implementation of a key-value validation method exists within your model. This violation of the API contract may result in the save operation being mis-interpretted by callers that rely on the availability of the error.");
|
||||
} else {
|
||||
// We have been cancelled while the save is in progress -- bail
|
||||
success = NO;
|
||||
}
|
||||
}];
|
||||
|
||||
if (! success) {
|
||||
if (error) *error = localError;
|
||||
return NO;
|
||||
}
|
||||
|
||||
if (! contextToSave.parentContext && contextToSave.persistentStoreCoordinator == nil) {
|
||||
RKLogWarning(@"Reached the end of the chain of nested managed object contexts without encountering a persistent store coordinator. Objects are not fully persisted.");
|
||||
return NO;
|
||||
}
|
||||
contextToSave = contextToSave.parentContext;
|
||||
}
|
||||
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (BOOL)saveContext:(NSManagedObjectContext *)context error:(NSError **)error
|
||||
{
|
||||
__block BOOL success = YES;
|
||||
__block NSError *localError = nil;
|
||||
if (self.savesToPersistentStore) {
|
||||
success = [context saveToPersistentStore:&localError];
|
||||
success = [self saveContextToPersistentStore:context error:&localError];
|
||||
} else {
|
||||
[context performBlockAndWait:^{
|
||||
success = ([self isCancelled]) ? NO : [context save:&localError];
|
||||
|
||||
@@ -361,6 +361,9 @@ static inline NSManagedObjectID *RKObjectIDFromObjectIfManaged(id object)
|
||||
__block RKMappingResult *mappingResult = nil;
|
||||
self.operationQueue = [NSOperationQueue new];
|
||||
[self.managedObjectContext performBlockAndWait:^{
|
||||
// We may have been cancelled before we made it onto the MOC's queue
|
||||
if ([self isCancelled]) return;
|
||||
|
||||
// Configure the mapper
|
||||
self.mapperOperation = [[RKMapperOperation alloc] initWithRepresentation:sourceObject mappingsDictionary:self.responseMappingsDictionary];
|
||||
self.mapperOperation.delegate = self.mapperDelegate;
|
||||
|
||||
Reference in New Issue
Block a user