2012-05-14 31 views
6

Cách tôi đang cố gắng xóa nhiều bộ 10.000+ NSManagedObjects chỉ là quá nhiều bộ nhớ (khoảng 20MB byte trực tiếp) và ứng dụng của tôi đang bị xóa bỏ. Dưới đây là việc triển khai phương pháp xóa:Cách hiệu quả nhất để xóa một số lượng lớn (10.000+) đối tượng trong Dữ liệu chính là gì?

+ (void)deleteRelatedEntitiesInManagedObjectContext:(NSManagedObjectContext *)context 
{ 
    NSFetchRequest *fetch = [[NSFetchRequest alloc] init]; 
    [context setUndoManager:nil]; 

    [fetch setEntity:[NSEntityDescription entityForName:NSStringFromClass(self) inManagedObjectContext:context]]; 
    [fetch setIncludesPropertyValues:NO]; 

    NSError *error = nil; 
    NSArray *entities = [context executeFetchRequest:fetch error:&error]; 

    NSInteger deletedCount = 0; 
    for (NSManagedObject *item in entities) { 
     [context deleteObject:item]; 
     deletedCount++; 

     if (deletedCount == 500) { 
      [context save:&error]; 
      deletedCount = 0; 
     } 
    } 

    if (deletedCount != 0) { 
     [context save:&error]; 
    } 
} 

Tôi đã thử: -setFetchBatchSize, nhưng thậm chí còn sử dụng nhiều bộ nhớ hơn.

Điều gì sẽ là một cách hiệu quả hơn về bộ nhớ để thực hiện việc này?

+0

Tối ưu hóa hoạt động: 1. đường xóa -setIncludesPropertyValues; 2. thay đổi giới hạn tìm nạp từ 500 đến 2500 (thực sự!); 3.sử dụng @autoreleasepool –

Trả lời

11

EDIT: Chỉ cần nhìn 2015 WWDC "Có gì mới trong Core Data" (đó là lúc nào cũng là video đầu tiên tôi xem, nhưng tôi đã rất bận rộn trong năm nay) và họ công bố một API mới: NSBatchDeleteRequest rằng nên hiệu quả hơn bất kỳ giải pháp nào trước đây.


Hiệu quả có nhiều ý nghĩa và thường có nghĩa là một số loại thỏa thuận. Ở đây, tôi giả sử bạn chỉ muốn chứa bộ nhớ trong khi xóa.

Dữ liệu chính có tùy chọn hiệu suất, vượt ra ngoài phạm vi của bất kỳ câu hỏi SO nào.

Cách quản lý bộ nhớ phụ thuộc vào cài đặt cho managedObjectContext và fetchRequest của bạn. Xem các tài liệu để xem tất cả các tùy chọn. Đặc biệt, mặc dù, bạn nên giữ những điều này trong tâm trí.

Ngoài ra, hãy nhớ khía cạnh hiệu suất. Loại hoạt động này nên được thực hiện trên một sợi riêng biệt.

Ngoài ra, lưu ý rằng phần còn lại của đồ thị đối tượng của bạn cũng sẽ đi vào chơi (vì cách CoreData xử lý xóa các đối tượng có liên quan.

Về tiêu thụ bộ nhớ, có hai tài sản trên MOC đặc biệt chú ý đến .Trong khi có rất nhiều ở đây, nó không phải là gần như toàn diện. Nếu bạn muốn thực sự thấy những gì đang xảy ra, NSLog MOC của bạn ngay trước và sau mỗi thao tác lưu. Đặc biệt, log registeredObjects và deletedObjects.

  1. MOC có danh sách các đối tượng đã đăng ký. Theo mặc định, nó không giữ lại các đối tượng đã đăng ký. Tuy nhiên, nếu retainsRegisteredObjects là YES, nó sẽ giữ lại tất cả các đối tượng đã đăng ký.

  2. Để xóa cụ thể, setPropagatesDeletesAtEndOfEvent cho MOC biết cách xử lý các đối tượng liên quan. Nếu bạn muốn chúng được xử lý bằng cách lưu, bạn cần đặt giá trị đó thành NO. Nếu không, nó sẽ đợi cho đến khi sự kiện hiện tại được thực hiện

  3. Nếu bạn có bộ đối tượng thực sự lớn, hãy xem xét sử dụng fetchLimit. Trong khi các lỗi không chiếm nhiều bộ nhớ, chúng vẫn mất một số, và hàng ngàn lần tại một thời điểm không đáng kể. Nó có nghĩa là tìm nạp nhiều hơn, nhưng bạn sẽ giới hạn số lượng bộ nhớ

  4. Cũng nên xem xét, bất cứ khi nào bạn có vòng nội bộ lớn, bạn nên sử dụng hồ bơi tự động của riêng mình.

  5. Nếu MOC này có cha/mẹ, việc lưu chỉ di chuyển những thay đổi đó cho phụ huynh. Trong trường hợp này, nếu bạn có một MOC mẹ, bạn chỉ làm cho cái đó phát triển.

Đối với giới hạn bộ nhớ, xem xét việc này (không nhất thiết phải tốt nhất cho trường hợp của bạn - có rất nhiều Core tùy chọn Data - chỉ có bạn mới biết điều gì là tốt nhất cho tình hình của bạn, dựa trên tất cả các tùy chọn bạn

Tôi đã viết một danh mục trên NSManagedObjectContext mà tôi sử dụng để tiết kiệm khi tôi muốn đảm bảo lưu vào cửa hàng sao lưu, rất giống với điều này.Nếu bạn không sử dụng phân cấp MOC, bạn không cần nó, nhưng ... thực sự không có lý do gì để KHÔNG sử dụng một hệ thống phân cấp (trừ khi bạn bị ràng buộc với iOS cũ)

- (BOOL)cascadeSave:(NSError**)error { 
    __block BOOL saveResult = YES; 
    if ([self hasChanges]) {    
     saveResult = [self save:error]; 
    } 
    if (saveResult && self.parentContext) { 
     [self.parentContext performBlockAndWait:^{ 
      saveResult = [self.parentContext cascadeSave:error]; 
     }]; 
    } 
    return saveResult; 
} 

tôi đổi mã của bạn một chút ...

+ (void)deleteRelatedEntitiesInManagedObjectContext:(NSManagedObjectContext *)context 
{ 
    NSFetchRequest *fetch = [[NSFetchRequest alloc] init]; 
    [context setUndoManager:nil]; 

    [fetch setEntity:[NSEntityDescription entityForName:NSStringFromClass(self) inManagedObjectContext:context]]; 
    [fetch setIncludesPropertyValues:NO]; 
    [fetch setFetchLimit:500]; 

    NSError *error = nil; 
    NSArray *entities = [context executeFetchRequest:fetch error:&error]; 
    while ([entities count] > 0) { 
     @autoreleasepool { 
      for (NSManagedObject *item in entities) { 
       [context deleteObject:item]; 
      } 
      if (![context cascadeSave:&error]) { 
       // Handle error appropriately 
      } 
     } 
     entities = [context executeFetchRequest:fetch error:&error]; 
    } 
} 
+0

Câu trả lời hay, cảm ơn! –

+0

Sử dụng bể tự động, tôi đã giảm mức sử dụng bộ nhớ từ 20MB xuống còn 8MB. Các tầng thác tiết kiệm là một chút overkill cho tôi, nhưng tôi sẽ nhớ nó cho sau này. Tôi cũng đã xóa dòng -setIncludesPropertyValues ​​và sử dụng giới hạn tìm nạp. –

+0

Tôi có [câu hỏi] (http://stackoverflow.com/questions/42675933/nsmanagedobjectcontexts-propagatesdeletesatendofevent-set-to-false-causes-error) liên quan đến việc sử dụng 'propagatesDeletesAtEndOfEvent'. – LShi

0

Đây sẽ là một thử nghiệm thú vị, hãy thử sử dụng Magical Record. Có một phương pháp cắt ngắn trong đó có nghĩa vụ phải rất hiệu quả (tôi đã sử dụng nó trên các tập dữ liệu lớn đến 3000 hồ sơ mà không có vấn đề. Hãy thú vị để xem nó xử lý 10.000 như thế nào. dễ dàng hơn cho tính năng đó một mình, nếu bạn đã không thử nó, bạn nên. nó làm cho đối phó với Core Data rất nhiều và với mã ít hơn nhiều.

Hope this helps.

+0

Cảm ơn, tôi sẽ thử nó đôi khi. –

+0

Tôi đang làm việc dưới đây với khoảng 16 nghìn bản ghi trong CoreData và ứng dụng bị lỗi. Vấn đề bộ nhớ. [MagicalRecord saveWithBlock:^(NSManagedObjectContext * localContext) { [Mục MR_truncateAllInContext: localContext]; } hoàn thành:^(BOOL thành công, lỗi NSError *) { nếu (! IsEmpty (khối)) { khối (lỗi); } }]; –

0

tôi có không có nghĩa là thử nghiệm nó , nhưng nếu bộ nhớ là mối quan tâm chính của bạn, bạn có thể thử đóng gói các lô đó trong 500 lần xóa trong nhóm tự động trả thêm. Có thể bối cảnh đó: lưu sẽ tạo ra một vài đối tượng tự động không được phát hành cho đến khi bạn thực hiện xong vòng lặp chạy chu kỳ. Với hơn 10.000 hồ sơ, nó có thể tăng lên khá độc đáo.

+0

Cảm ơn! Tôi cũng sẽ thử cái này. –

0

Nếu bạn không muốn sử dụng API khác, hãy thử một tính năng khác của NSFetchRequest, fetchLimit, có thể kết hợp với fetchOffset. Tôi đã thấy điều này trong một trong các khóa học iTunes U iPad trong một ví dụ liên quan đến số lượng lớn crunching với Core Data.

NSInteger limit = 500; 
NSInteger count = [context countForFetchRequest:fetch error:nil]; 
[fetch setFetchLimit:limit]; 
for (int i=0; i < count/limit+1; i++) { 
    // do the fetch and delete and save 
} 

Bạn có thể điều chỉnh fetchLimit để đáp ứng yêu cầu bộ nhớ của bạn.

+0

Bạn có cơ hội nhớ khóa học nào không? Tôi quan tâm đến việc nhìn thấy nó ... Dù sao, tôi có giải pháp của bạn quá. –

1

Trong một khoảnh khắc của cảm hứng, tôi loại bỏ [fetch setIncludesPropertyValues:NO]; và nó là tốt. Từ các tài liệu:

Trong một bình thường lấy (includesPropertyValues ​​là YES), Core Data fetches các dữ liệu ID đối tượng và tài sản cho các hồ sơ phù hợp, lấp đầy bộ nhớ cache hàng với các thông tin, và trả về quản lý đối tượng như lỗi (xem returnsObjectsAsFaults). Những lỗi này được quản lý đối tượng, nhưng tất cả dữ liệu thuộc tính của chúng vẫn nằm trong bộ nhớ cache hàng cho đến khi lỗi được kích hoạt. Khi lỗi được kích hoạt, Dữ liệu lõi sẽ truy xuất dữ liệu từ bộ nhớ cache hàng — không cần phải quay lại cơ sở dữ liệu .

Tôi đã quản lý giảm byte được phân bổ trực tiếp xuống ~ 13MB, điều này tốt hơn.

1

NSBatchDeleteRequest Làm việc cho tôi; giảm thời gian xóa các đối tượng được quản lý theo hệ số 5, không tăng đột biến bộ nhớ.

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