Основные данные: избегать сохранения циклов во многих отношениях
Я все еще изучаю свой путь через разработку 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
Мой опыт заключается в том, что повторного сбоя только для элемента отдела достаточно, чтобы прервать цикл сохранения. Профилирующая память ясно показывает, что все связанные с ней сотрудники затем освобождаются, если они не сохранены в другом месте вашим кодом.