Основные данные: избегая сохранения циклов во многих отношениях - PullRequest
14 голосов
/ 21 октября 2010

Я все еще изучаю свой путь в разработке для iOS и работе с Core Data и только что натолкнулся на сохранение циклов.

Насколько я понимаю, прочитав Руководство по программированию Core Data, после того как вы закончите работатьс помощью отношения вы используете метод контекста управляемого объекта refreshObject:mergeChanges, чтобы гарантировать, что цикл сохранения нарушен.

Итак, допустим, у меня есть отношение ко многим между Департаментом и его Сотрудниками, и в моем кодеЯ получаю доступ к отношениям сотрудников из отдела. Значит ли это, что теперь мне нужно перебрать каждый объект сотрудника и вызвать метод refreshObject:mergeChanges?В коде это будет

for (Employee *anEmployee in department.employees) {
  //some code that accesses an employee's properties

  [context refreshObject:enEmployee mergeChanges:NO];
}

Кажется, что если я этого не сделаю, то каждый объект сотрудника, к которому я обращаюсь, теперь будет содержать ссылку на отдел, и у меня останется цикл сохранения.

Правильно ли мое понимание здесь?Является ли это стандартным подходом при работе со многими связями в базовых данных?Спасибо.

Ответы [ 3 ]

3 голосов
/ 25 октября 2010

Я написал несколько вспомогательных методов (см. Ниже), чтобы разорвать циклы сохранения для целого графа объектов, используя интроспекцию модели Entity. Вы можете использовать его после получения предупреждающего уведомления о памяти, чтобы освободить любую память, удерживаемую частью вашей базовой модели данных, доступной через этот конкретный объект.

@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
1 голос
/ 21 октября 2010

Как вы можете проверить в Циклы сохранения разрыва отношений , циклы сохранения необходимы для предотвращения освобождения нежелательных объектов.Это означает, что вы сохраняете объект, сохраненный во время его использования.

refreshObject:mergeChanges следует использовать, если вы закончили с этим объектом и хотите превратить его в неисправный, чтобы по возможности распоряжаться памятью.Он не обязательно освободит объект на другом конце отношения, он только установит флаг для основных данных, чтобы объект мог быть превращен в ошибку, если это необходимо.

0 голосов
/ 05 апреля 2013

Мой опыт показывает, что повторного сбоя только в подразделении достаточно, чтобы разорвать цикл сохранения.Профилирование памяти ясно показывает, что все связанные с ними сотрудники затем освобождаются, если они не сохраняются в другом месте вашим кодом.

...