Core Data: avoiding retain cycles in to-many relationships

后端 未结 3 917
感情败类
感情败类 2021-02-07 12:28

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

3条回答
  •  孤街浪徒
    2021-02-07 12:44

    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
    

提交回复
热议问题