RestKit/RestKit — pull request #1977 by aldelucca1 (2014-07-22 12:30:12+00:00)
Title: RKFetchRequestBlock causes infinite recursion when CoreData objects have...

... inverse relationships

When using a RKFetchRequestBlock to clean up CoreData entities that have been removed from a remote feed it is possible to crash due to infinite recursion if the entity being removed is related to another entity with an inverse relationship. This is caused by the following code:

```
static NSSet *RKGatherManagedObjectsFromObjectWithRelationshipMapping(id object, RKRelationshipMapping *relationshipMapping)
{
NSMutableSet *managedObjects = [NSMutableSet set];
NSSet *relationshipValue = RKFlattenCollectionToSet([object valueForKeyPath:relationshipMapping.destinationKeyPath]);
for (id relatedObject in relationshipValue) {
if ([relatedObject isKindOfClass:[NSManagedObject class]]) [managedObjects addObject:relatedObject];

if ([relationshipMapping.mapping isKindOfClass:[RKObjectMapping class]]) {
for (RKRelationshipMapping *childRelationshipMapping in [(RKObjectMapping *)relationshipMapping.mapping relationshipMappings]) {
[managedObjects unionSet:RKGatherManagedObjectsFromObjectWithRelationshipMapping(relatedObject, childRelationshipMapping)];
}
} else if ([relationshipMapping.mapping isKindOfClass:[RKDynamicMapping class]]) {
for (RKObjectMapping *objectMapping in [(RKDynamicMapping *)relationshipMapping.mapping objectMappings]) {
@try {
for (RKRelationshipMapping *childRelationshipMapping in objectMapping.relationshipMappings) {
[managedObjects unionSet:RKGatherManagedObjectsFromObjectWithRelationshipMapping(relatedObject, childRelationshipMapping)];
}
}
@catch (NSException *exception) {
continue;
}
}
}
}
return managedObjects;
}

```
Since the set is recreated for each invocation there is no way stopping the recursion if we encounter a relationship to the parent from the child. I fixed this in my local workspace as follows:

```
static void RKGatherManagedObjectsFromObjectWithRelationshipMapping(id object, RKRelationshipMapping *relationshipMapping, NSMutableSet *managedObjects)
{
NSSet *relationshipValue = RKFlattenCollectionToSet([object valueForKeyPath:relationshipMapping.destinationKeyPath]);
for (id relatedObject in relationshipValue) {
if ([managedObjects containsObject:relatedObject]) continue;
if ([relatedObject isKindOfClass:[NSManagedObject class]]) [managedObjects addObject:relatedObject];

if ([relationshipMapping.mapping isKindOfClass:[RKObjectMapping class]]) {
for (RKRelationshipMapping *childRelationshipMapping in [(RKObjectMapping *)relationshipMapping.mapping relationshipMappings]) {
RKGatherManagedObjectsFromObjectWithRelationshipMapping(relatedObject, childRelationshipMapping, managedObjects);
}
} else if ([relationshipMapping.mapping isKindOfClass:[RKDynamicMapping class]]) {
for (RKObjectMapping *objectMapping in [(RKDynamicMapping *)relationshipMapping.mapping objectMappings]) {
@try {
for (RKRelationshipMapping *childRelationshipMapping in objectMapping.relationshipMappings) {
RKGatherManagedObjectsFromObjectWithRelationshipMapping(relatedObject, childRelationshipMapping, managedObjects);
}
}
@catch (NSException *exception) {
continue;
}
}
}
}
}

```
By retaining the knowledge of the entities we have already processed we can safely continue the loop, removing the possibility of infinite recursion