2010-10-21 26 views
14

Tôi vẫn đang học cách phát triển iOS và làm việc với Dữ liệu cốt lõi và đã đi qua các chu kỳ lưu giữ. Đó là sự hiểu biết của tôi khi đọc Hướng dẫn lập trình dữ liệu cốt lõi sau khi bạn hoàn thành công việc với một mối quan hệ, bạn sử dụng phương thức bối cảnh đối tượng được quản lý refreshObject:mergeChanges để đảm bảo rằng chu trình giữ lại bị hỏng. Vì vậy, cho phép nói rằng tôi có một mối quan hệ rất nhiều giữa Sở và Nhân viên của nó, và trong mã của tôi, tôi truy cập mối quan hệ nhân viên từ bộ phận, điều đó có nghĩa là bây giờ tôi sẽ phải lặp qua từng đối tượng nhân viên và gọi refreshObject:mergeChanges phương pháp? Trong mã này sẽ làDữ liệu cốt lõi: tránh giữ lại chu kỳ trong các mối quan hệ với nhiều người

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

    [context refreshObject:enEmployee mergeChanges:NO]; 
} 

Dường như nếu tôi không làm điều đó, mỗi đối tượng nhân viên tôi truy cập bây giờ sẽ có tham chiếu đến bộ phận và tôi sẽ kết thúc với chu kỳ lưu giữ.

Sự hiểu biết của tôi có chính xác ở đây không? Đây có phải là cách tiếp cận tiêu chuẩn khi giao dịch với nhiều mối quan hệ trong Dữ liệu cốt lõi không? Cảm ơn.

Trả lời

1

Khi bạn có thể kiểm tra tại Breaking Relationship Retain Cycles, các chu trình giữ lại là cần thiết để ngăn chặn deallocation các đối tượng không mong muốn. Nó có nghĩa là bạn giữ đối tượng được giữ lại trong khi bạn đang sử dụng nó.

Các refreshObject:mergeChanges nên được sử dụng nếu bạn đang thực hiện với đối tượng đó và bạn muốn biến nó thành lỗi, để vứt bỏ bộ nhớ nếu có thể. Nó sẽ không nhất thiết phải giải phóng đối tượng ở đầu kia của mối quan hệ, nó sẽ chỉ thiết lập một lá cờ cho dữ liệu cốt lõi mà đối tượng có thể bị biến thành lỗi nếu cần thiết.

+0

Vì vậy, điều này có nghĩa là mỗi khi tôi làm việc với các đối tượng trong mối quan hệ tôi sẽ cần phải gọi phương thức đó trên đối tượng? –

+0

Không bắt buộc phải làm điều đó, mặc dù nó được khuyến khích cao trong trường hợp bạn có một cửa hàng lớn và thường xuyên truy cập 'NSManagedObjects' riêng biệt, và không cần phải giữ chúng trong bộ nhớ (Bạn có thể để chúng trở thành lỗi lầm)). Hãy tưởng tượng CoreData như là một "hệ thống thu gom rác", trên một môi trường thu gom rác bạn chỉ vứt bỏ bộ nhớ nếu bạn quan tâm đến việc sử dụng bộ nhớ. 'RefreshObject: mergeChanges' sẽ giải phóng đối tượng thay vì để nó vào hệ thống thu gom rác. Công cụ thu gom rác này chỉ là sự tương tự để bạn hiểu quy trình. – vfn

3

Tôi đã viết một vài phương thức trợ giúp (xem bên dưới) để phá vỡ vòng lặp giữ lại cho toàn bộ đồ thị đối tượng bằng cách sử dụng nội suy của mô hình Entity. Bạn có thể sử dụng nó sau khi nhận được thông báo cảnh báo bộ nhớ để giải phóng bất kỳ bộ nhớ nào được giữ bởi một phần của mô hình dữ liệu cốt lõi của bạn có thể truy cập thông qua đối tượng cụ thể đó.

@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

Cảm ơn! Điều này có vẻ rất hữu ích. Không trả lời câu hỏi ban đầu của tôi vì vậy tôi sẽ không chấp nhận câu trả lời này nhưng tôi vẫn cảm ơn bạn. –

+0

Điều này có thực sự cần thiết không? Tôi nghĩ rằng dữ liệu cốt lõi sẽ tự động phản ứng với cảnh báo bộ nhớ. Không phải là những phương pháp trợ giúp này chỉ tạo ra một chi phí khi một cảnh báo bộ nhớ được nâng lên? – vfn

+0

Dữ liệu lõi không thể tự động giải phóng các đối tượng là một phần của vòng lặp lưu giữ (không có bộ sưu tập rác tự động, không có sẵn trên iPhone), bạn phải phá vỡ vòng giữ đầu tiên bằng cách đặt lại ngữ cảnh hoặc sử dụng refreshObject: mergeChanges :Không có cuộc gọi. –

0

Kinh nghiệm của tôi là chỉ tái phạm lỗi đơn vị của bộ phận là đủ để phá vỡ chu kỳ lưu giữ. Bộ nhớ cấu hình hiển thị rõ ràng rằng tất cả các nhân viên có liên quan sau đó được giải phóng, trừ khi chúng được giữ lại ở nơi khác bằng mã của bạn.

Các vấn đề liên quan