5

約53 MBのiPad上でJSONファイルを解析しています。解析は、私がSAXパーサであり、このようにそれを設定しているYajlparserを使用しています、正常に動作している: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警告なしで完璧に動作します。

私はInstrumentsでプロファイリングを開始し、多くのCFNumber割り当てを見つけました(私は2分後にInstrumentsを停止しました。クラッシュとCFNumberが約60Mb以上になるまで前に実行しました)。

CFNumber allocations

CFNumber詳細を開いた後、それは割り当ての膨大なリストを示しました。そのうちの一つは、私に次のことを示した:

CFNumber alloc 1

、ここで別の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パーサーの1つの潜在的な問題は、プリミティブなJSON値(String、True、False、Number、Null)に対してNSObjectsを渡すことです。つまり、内部的にNSObjectを割り当てる必要があります。おそらく、これらのオブジェクトも自動解放プールに入れられます。より良いアプローチはJSONプリミティブ値をパーザのデリゲートに渡すために何も割り当てません。しばしば(CD管理オブジェクトの作成の場合と同様に)、これも不要です。いずれにしても、CD管理対象オブジェクトはそのプロパティのコピーを作成します。要約すると、IMO、YAJLはあなたの問題に最適ではないようです。 – CouchDeveloper

答えて

5

Efficiently Importing Dataのアップルのコアデータのドキュメント、特に「ピークメモリフットプリントの削減」を参照してください。

メモリに一度に新しいエンティティが多数含まれていないことを確認する必要があります。データの解析中に定期的にコンテキストを保存およびリセットし、自動解放プールをうまく使用する必要があります。

一般sudoのコードはこのようなものになるだろう:

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

だから、基本的に、あなたのメインループまたは解析するコードの周りに自動解放プールを置きます。 NSManagedObjectインスタンスをいくつ作成したかを数え、管理対象オブジェクトコンテキストを定期的に保存してリセットして、これらのメモリをフラッシュします。これは、あなたのメモリフットプリントを維持する必要があります。数字100は任意であり、異なる値で試してみるとよいでしょう。

バッチごとにコンテキストを保存するので、何か問題が生じた場合に備えて店舗の一時的なコピーにインポートして部分的なインポートを残すことができます。すべてが終わったら、元の店を上書きすることができます。

+0

感謝のXcode(NSManagedObjectのサブクラス)によって作成されたエンティティーです。しかし、すべてが保存した後、私は' '[コンテキストのリセット]をやっている私がいます。 'dispatch_async(dispatch_get_global_queue内部のメインループ(DISPATCH_QUEUE_PRIORITY_HIGH、0)、^ {}'、しかし、私が使用しているとき、私は私がそれを気にする必要はありませんと思ったので、私は、 '@ autoreleasepool'-ブツを使用していませんよ。ARC – gasparuff

+1

(ない外、ループ内) '@のautoreleasepool'であなたのメインループ内のコードをラップしてみ' @ autoreleasepool' GCD内部 –

+0

。?[のNSNumber numberWithIntegerを:...]'ライン思われるメモリスパイクを引き起こしている可能性があります。そのため、GCDブロックが終了するまで流出しない自動解放プールに多くの数値が追加されている可能性があります。したがって、このプール数を減らすために、メインループ本体のコードの周りに独自の自動解放プールを配置する必要があります。 – gasparuff

1

[self.managedObjectContext refreshObject:obj refreshChanges:NO]を使用すると、特定の量の挿入操作が実行されます。これは、NSManagedObjectsを障害に変え、いくらかのメモリを解放します。

Apple Docs on provided methods

+0

私はこれを試しましたが、残念ながらこれは本当に助けにはなりませんでした。そしてそれは '[self.managedObjectContext refreshObject:obj mergeChanges:NO]' :-)です – gasparuff

関連する問題