2010-10-21 62 views
14

我仍在學習通過iOS開發和使用核心數據的方式,並剛剛遇到保留週期。核心數據:避免在多對多關係中保留週期

通過閱讀Core Data Programming Guide,我的理解是,在完成關係後,使用託管對象上下文方法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中的多對多關係時,這是一種標準方法嗎?謝謝。

回答

1

正如你可以檢查Breaking Relationship Retain Cycles,保留週期是必要的,以防止不需要的對象的重新分配。這意味着您在使用時保留該對象。

如果您已完成該對象並且想要將其轉換爲故障,並在可能的情況下處理內存,則應使用refreshObject:mergeChanges。它不一定會在關係的另一端釋放對象,它只會爲核心數據設置一個標誌,以便在必要時可以將對象轉換爲錯誤。

+0

所以這意味着每次我處理關係中的對象時,我都需要在對象上調用該方法? – 2010-10-21 16:34:41

+0

這不是強制性的,儘管強烈建議你有一個相當大的商店,並經常訪問不同的NSManagedObjects,並且不需要將它們保存在內存中(你可以讓它們成爲錯誤)。將CoreData想象成一個「垃圾收集器系統」,在垃圾收集環境中,如果您擔心內存使用情況,則只處置內存。 'refreshObject:mergeChanges'會釋放該對象,而不是讓它進入垃圾收集器系統。這垃圾收集器的東西只是一個類比,讓你瞭解過程。 – vfn 2010-10-21 22:33:41

3

我已經寫了幾個幫助器方法(見下文)通過使用實體模型的內省來打破整個對象圖的保留循環。您可以在收到內存警告通知後使用它,以釋放通過該特定對象可訪問的核心數據模型部分所持有的內存。

@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 
+0

謝謝!這看起來非常有幫助。不回答我最初的問題,所以我不會接受這個答案,但我仍然感謝你。 – 2010-10-26 13:03:23

+0

這真的有必要嗎?我認爲核心數據會自動響應內存警告。這些助手方法是不是隻在引發內存警告時創建開銷? – vfn 2010-10-27 00:51:46

+0

不幸的是,核心數據無法自動釋放屬於保留循環(即沒有自動垃圾回收,在iPhone上不可用)的對象,因此必須通過重置上下文或使用refreshObject來首先中斷保留循環:mergeChanges :沒有呼叫。 – 2010-10-27 14:12:42

0

我的經驗是,只有部門實體的再次斷層足以打破保留週期。對內存進行性能分析清楚地表明,除非您的代碼保留在其他地方,否則所有相關員工都將被釋放。