5

我解析一個約53 MB的iPad上的JSON文件。解析工作正常,我使用Yajlparser這是一個SAX解析器,並設置它是這樣的:巨大的內存消耗,同時解析JSON和創建NSManagedObjects

NSData *data = [NSData dataWithContentsOfFile:path options:NSDataReadingMappedAlways|NSDataReadingUncached error:&parseError]; 
    YAJLParser *parser = [[YAJLParser alloc] init]; 
    parser.delegate = self; 
    [parser parse:data]; 

一切工作細到現在爲止,但JSON文件變大,現在我突然在iPad 2上遇到內存警告。它收到4條內存警告,然後只是崩潰。在iPad 3上,它完美無缺地運行,沒有任何mem警告。

我已經開始使用儀器進行分析,並發現了很多CFNumber分配(我在幾分鐘後停止了儀器,之前運行過它直到崩潰,CFNumber事件約爲60 mb或更多)。

CFNumber allocations

打開CFNumber細節後,出現了一個巨大的分配名單。其中一人向我展示了以下內容:

CFNumber alloc 1

,另外一個位置:

CFNumber alloc 2

那我做錯了嗎?那麼這個數字是多少(例如最後一張圖片中的72.8%)呢?我正在使用ARC,因此我沒有執行任何發佈或保留任何操作。

感謝您的幫助。 乾杯

編輯:我已經問如何解析這裏這麼大的文件的問題:iPad - Parsing an extremely huge json - File (between 50 and 100 mb)
所以解析本身似乎要被罰款。

+0

這個數字意味着它的72.8%,可能是這部分代碼是造成你所面臨的重大課題。 (其大部分時間指向正確的方向,除了少數幾個)。 僅僅因爲你使用的是ARC,並不意味着100%無錯誤/無泄漏代碼。例如循環引用仍然可能導致代碼泄漏。 嘗試運行內存分析器,看看你的「虛擬內存」是否變得太大。請張貼您的反饋 – nsuinteger

+0

根據我的理解,CoreFoundation會盡量減少Numbers和Strings等對象的實例數量。屬性「kundennr」是否被任何機會定義爲「複製」?看起來你可能在每次分配該屬性和「currentWarengruppeVK」時複製副本。這將否定CoreFoundation提供的內置效率。 – Saltymule

+0

YAJL解析器的一個潛在問題是它傳遞了原始JSON值(String,True,False,Number,Null)的NSObject。也就是說,它內部必須爲其分配NSObject。很可能,這些對象也將被放入autorelease池中。更好的方法不會分配任何東西來將JSON原始值傳遞給解析器的委託。通常(比如創建CD託管對象),這也是不必要的。無論如何,CD託管對象將爲其屬性創建副本。簡而言之:IMO,YAJL對於你的問題似乎並不理想。 – CouchDeveloper

回答

5

查看關於Efficiently Importing Data的Apple核心數據文檔,特別是「減少峯值內存佔用」。

您需要確保您一次不會在內存中擁有太多新實體,這需要在解析數據時定期保存和重置您的上下文,以及使用autorelease池。

一般sudo的代碼將是這樣的:

while (there is new data) { 
    @autoreleasepool { 
     importAnItem(); 
     if (we have imported more than 100 items) { 
      [context save:...]; 
      [context reset]; 
     } 
    } 
} 

因此,基本上,把一個自動釋放池在你的主迴路或分析代碼。計算已創建多少個實例,並定期保存並重置託管對象上下文以清除這些內存不足。這應該保持你的記憶足跡。數字100是任意的,你可能想要嘗試不同的值。

由於您正在爲每個批次保存上下文,因此您可能需要導入到商店的臨時副本中,以防出現問題並導致部分導入。一切完成後,您可以覆蓋原始商店。

+0

感謝您的回答,但每次保存後我都會執行'[context reset]'。一個'dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH,0),^ {}'裏面的主循環,但我沒有使用'@ autoreleasepool'-thingy,因爲我認爲在使用時我不需要關心它ARC。 – gasparuff

+1

嘗試使用'@ autoreleasepool'將代碼包裝到主循環中(在循環內部,而不是在外部) –

+0

使用GCD內的@ autoreleasepool' – gasparuff

1

嘗試在一定數量的插入操作後使用[self.managedObjectContext refreshObject:obj refreshChanges:NO]。這會將NSManagedObjects轉換成錯誤並釋放一些內存。

Apple Docs on provided methods

+0

我試過這個,不幸的是這並沒有真正的幫助。它是'[self.managedObjectContext refreshObject:obj mergeChanges:NO]':-) – gasparuff