2012-05-14 34 views
6

我試圖刪除多組10.000+ NSManagedObjects的方式太內存密集(大約20MB活字節),我的應用程序正在被拋棄。這裏是刪除方法的實現:在覈心數據中刪除大量(10.000+)對象的最有效方法是什麼?

+ (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]; 
    } 
} 

我試過了:-setFetchBatchSize,但是還有更多的內存被使用。

什麼會是一個更有效的方法來做到這一點?

+0

優化工作:1.刪除-setIncludesPropertyValues行; 2.將獲取限制從500更改爲2500(真的!); 3。使用@autoreleasepool –

回答

11

編輯:剛看了2015年的WWDC「什麼核心數據的新」(它總是第一個視頻我看,但今年我已經很繁忙),他們宣佈了一個新的API:NSBatchDeleteRequest應該比以前的解決方案效率更高。


高效有多重意義,並且通常意味着某種權衡。在這裏,我假設你只是想在刪除時包含內存。

核心數據有很多的性能選項,超出了任何單個SO問題的範圍。

如何管理內存取決於您的managedObjectContext和fetchRequest的設置。查看文檔以查看所有選項。不過,特別是,你應該記住這些事情。

另外,請記住性能方面。這種類型的操作應該在單獨的線程上執行。

另外,請注意你的對象圖的其餘部分也將開始發揮作用(因爲CoreData如何處理相關對象的缺失。

關於內存的消耗,也有對特別要注意MOC兩個屬性。雖然這裏有很多,但它並不接近全面。如果你想真正看到發生了什麼,在每次保存操作之前和之後NSLog記錄你的MOC。特別是,log registeredObjects和deletedObjects。

  1. MOC有一個註冊對象的列表。默認情況下,它不保留已註冊的對象。但是,如果retaininsRegisteredObjects爲YES,它將保留所有註冊的對象。

  2. 對於特別的刪除,setPropagatesDeletesAtEndOfEvent告訴MOC如何處理相關的對象。如果您希望用保存處理它們,則需要將該值設置爲NO。否則,它將等待,直到當前事件完成

  3. 如果您有非常大的對象集,請考慮使用fetchLimit。雖然缺點並不需要很多記憶,但它們仍然需要一些,而且每次數千個並不重要。這意味着更多的提取,但你會限制內存的數量

  4. 還要考慮,任何時候你有大的內部循環,你應該使用自己的自動釋放池。

  5. 如果此MOC具有父級,則只會將這些更改移至父級。在這種情況下,如果你有一個母公司的MOC,你只是讓這個成長。

限制內存,考慮這個(不一定是最適合你的情況 - 有很多核心數據選項 - 只有你自己知道什麼是最適合你的情況,根據你提供的選項在其他地方使用

我在NSManagedObjectContext上編寫了一個類別,當我想要確保保存到後備存儲區時,我用它來保存,與此非常相似如果不使用MOC層次結構,需要它,但是......真的沒有理由不使用層次結構(除非你被綁定到老的iOS)

- (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; 
} 

我修改你的代碼一點點......

+ (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

很好的答案,謝謝! –

+0

使用autorelease池,我將內存使用量從20MB減少到了8MB。級聯保存對我來說有點矯枉過正,但我​​會在以後記住它。我還刪除了-setIncludesPropertyValues行並使用了獲取限制。 –

+0

我有[使用'propagatesDeletesAtEndOfEvent']的[一個問題](http://stackoverflow.com/questions/42675933/nsmanagedobjectcontexts-propagatesdeletesatendofevent-set-to-false-causes-error)。 – LShi

0

這將是一個有趣的測試,嘗試使用Magical Record。在那裏有一個截斷方法,應該是非常高效的(我已經在數據集上使用了3000條記錄,沒有問題,有興趣看看它如何處理10,000條記錄)

我不會只用它單獨該功能,如果你還沒有嘗試過,你應該,這使得處理核心數據,以便更容易,用少得多的代碼。

希望這有助於。

+0

謝謝,我會在某個時候嘗試。 –

+0

我正在做下面的CoreData中的大約16K記錄和應用程序崩潰。內存問題。 [MagicalRecord saveWithBlock:^(NSManagedObjectContext * localContext){ [Item MR_truncateAllInContext:localContext];如果(!IsEmpty(block)){ block(error);}如果(BOOL成功,NSError *錯誤){ }完成: } }]; –

0

我絕對沒有測試它,但是如果內存是你主要關心的問題,你可以嘗試在額外的自動釋放池中封裝那些批次的500次刪除。可能是context:save創建了很多自動釋放的對象,直到完成運行循環纔會釋放週期。憑藉10 000多條記錄,它可以很好地相加。

+0

謝謝!我也會試試這個。 –

0

如果您不想使用其他API,請嘗試使用NSFetchRequest,fetchLimit的其他功能,也可以結合fetchOffset。我在一個iTunes U iPad課程中看到過這個例子,這個例子涉及到大量的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 
} 

您可以調整fetchLimit以滿足您的記憶需求。

+0

你有沒有想過哪個課程是?我有興趣看到它......無論如何,我也必須解決你的問題。 –

1

的靈感的時刻,我刪除[fetch setIncludesPropertyValues:NO];,這是很好的。從該文檔:

在正常抓取(includesPropertyValues爲是),核心數據 提取的對象ID和屬性數據用於匹配的記錄, 填充用的信息的行高速緩存,並返回管理對象 如錯誤(請參閱returnsObjectsAsFaults)。這些故障管理對象,但其所有屬性數據仍位於行緩存 ,直到故障被觸發。當錯誤被觸發時,核心數據從行高速緩存中檢索數據 - 不需要返回數據庫 。

我設法減少分配的活動字節大約13MB,這是更好的。

1

NSBatchDeleteRequest爲我工作;將刪除管理對象的時間減少了5倍,沒有內存峯值。

相關問題