Основные данные: избегать сохранения циклов во многих отношениях

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

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

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

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

  [context refreshObject:enEmployee mergeChanges:NO];
}

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

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

Ответы

Ответ 1

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

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

Ответ 2

Я написал несколько вспомогательных методов (см. ниже), чтобы разбить циклы сохранения для целого графика объектов, используя интроспекцию модели 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

Ответ 3

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