核心数据:在多对多关系中避免保留周期

我仍然在通过iOS开发和Core Data的工作学习,并刚刚遇到保留周期。

通过阅读“核心数据编程指南”,我了解到,在完成关系后,可以使用托pipe对象上下文方法refreshObject:mergeChanges来确保保留周期已经中断。

因此,可以说我有一个部门及其雇员之间的一对多关系,并在我的代码中访问部门的雇员关系,这是否意味着我现在需要遍历每个员工对象,并调用refreshObject:mergeChanges方法? 在代码中,这将是

 for (Employee *anEmployee in department.employees) { //some code that accesses an employee's properties [context refreshObject:enEmployee mergeChanges:NO]; } 

看来,如果我不这样做,我访问的每个员工对象现在将包含对部门的引用,并且我将以保留周期结束。

我的理解在这里正确吗? 在处理Core Data中的多对多关系时,这是一种标准方法吗? 谢谢。

正如你可以在Breaking Relationship Retain Cycles中查看的那样,保留周期对于防止不需要的对象的重新分配是必要的。 这意味着您在使用时保留该对象。

如果你已经完成了这个对象并且你想把它变成错误的话,应该使用refreshObject:mergeChanges来尽可能地处置内存。 它不一定会在关系的另一端释放对象,只会为核心数据设置一个标志,必要时可以将对象转换为错误。

我已经写了一些帮助器方法(见下文),通过使用实体模型的内省来打破整个对象图的保留循环。 您可以在收到内存警告通知后使用它,以释放通过该特定对象可访问的核心数据模型部分所持有的内存。

 @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 

我的经验是,只有部门实体的再次断层足以打破保留周期。 对内存进行性能分析清楚地表明,所有相关的员工都被释放,除非他们被代码保留在其他地方。