I\'m still learning my way through iOS development and working with Core Data and have just come across retain cycles.
It is my understanding from reading the Core Data
I've written a couple of helper methods (see below) to break the retain loops for a whole graph of objects by using introspection of the Entity model. You can use it after receiving a memory warning notification to release any memory held by the part of your core data model accessible through that particular object.
@interface CoreDataHelper(Private)
+ (void)faultObjectImpl:(NSManagedObject *)managedObject mergeChanges:(FaultChangeBehaviour)mergeChanges;
+ (void)faultObjectGraphForObject:(NSManagedObject *)managedObject handledObjects:(NSMutableArray *)handledObjects mergeChanges:(FaultChangeBehaviour)mergeChanges;
@end
@implementation CoreDataHelper
typedef enum FaultChangeBehaviour {
FaultChangeBehaviourIgnore,
FaultChangeBehaviourReapply,
FaultChangeBehaviourMerge
} FaultChangeBehaviour;
+ (void)faultObjectGraphForObject:(NSManagedObject *)managedObject keepChanges:(BOOL)keepChanges {
NSMutableArray *handledObjects = [NSMutableArray arrayWithCapacity:64];
FaultChangeBehaviour mergeBehaviour = keepChanges ? FaultChangeBehaviourReapply : FaultChangeBehaviourIgnore;
[self faultObjectGraphForObject:managedObject handledObjects:handledObjects mergeChanges:mergeBehaviour];
}
+ (void)refreshObject:(NSManagedObject *)managedObject {
[self faultObjectImpl:managedObject mergeChanges:FaultChangeBehaviourMerge];
}
+ (void)refreshObjectGraphForObject:(NSManagedObject *)managedObject {
NSMutableArray *handledObjects = [NSMutableArray arrayWithCapacity:64];
[self faultObjectGraphForObject:managedObject handledObjects:handledObjects mergeChanges:FaultChangeBehaviourMerge];
}
@end
@implementation CoreDataHelper(Private)
+ (void)faultObjectImpl:(NSManagedObject *)managedObject mergeChanges:(FaultChangeBehaviour)mergeChanges {
//Only fault if the object is not a fault yet and is not in a modified state or newly inserted (not saved yet)
BOOL isFault = [managedObject isFault];
BOOL isTemporary = [[managedObject objectID] isTemporaryID];
BOOL isUpdated = [managedObject isUpdated];
NSDictionary *changedValues = [managedObject changedValues];
if (isUpdated && (mergeChanges == FaultChangeBehaviourIgnore)) {
NSLog(@"Warning, faulting object of class: %@ with changed values: %@. The changes will be lost!",
NSStringFromClass([managedObject class]), changedValues);
}
if (!isFault && !isTemporary) {
[[managedObject managedObjectContext] refreshObject:managedObject mergeChanges:(mergeChanges == FaultChangeBehaviourMerge)];
if (mergeChanges == FaultChangeBehaviourReapply) {
for (NSString *key in changedValues) {
id value = [changedValues objectForKey:key];
@try {
[managedObject setValue:value forKey:key];
} @catch (id exception) {
NSLog(@"Could not reapply changed value: %@ for key: %@ on managedObject of class: %@", value, key, NSStringFromClass([managedObject class]));
}
}
}
}
}
+ (void)faultObjectGraphForObject:(NSManagedObject *)managedObject handledObjects:(NSMutableArray *)handledObjects mergeChanges:(FaultChangeBehaviour)mergeChanges {
if (managedObject != nil && ![managedObject isFault] && ![handledObjects containsObject:[managedObject objectID]]) {
[handledObjects addObject:[managedObject objectID]];
NSEntityDescription *entity = [managedObject entity];
NSDictionary *relationShips = [entity relationshipsByName];
NSArray *relationShipNames = [relationShips allKeys];
for (int i = 0; i < relationShipNames.count; ++i) {
NSString *relationShipName = [relationShipNames objectAtIndex:i];
if (![managedObject hasFaultForRelationshipNamed:relationShipName]) {
id relationShipTarget = [managedObject valueForKey:relationShipName];
NSRelationshipDescription *relationShipDescription = [relationShips objectForKey:relationShipName];
if ([relationShipDescription isToMany]) {
NSSet *set = [NSSet setWithSet:relationShipTarget];
for (NSManagedObject* object in set) {
[self faultObjectGraphForObject:object handledObjects:handledObjects mergeChanges:mergeChanges];
}
} else {
NSManagedObject *object = relationShipTarget;
[self faultObjectGraphForObject:object handledObjects:handledObjects mergeChanges:mergeChanges];
}
}
}
[self faultObjectImpl:managedObject mergeChanges:mergeChanges];
}
}
@end