8

私は今、厄介なCoreDataの問題で約2週間立ち往生しています。たくさんのブログ投稿、記事、SOの質問/回答を読みましたが、それでも問題を解決できません。

私はたくさんのテストを実行し、大きな問題を小さな問題に減らすことができました。大きな説明になるので、私と一緒にいてください!

問題-データモデル

次のデータモデルを取得する必要があります

オブジェクトAはオブジェクトBと1対多の関係にあり、オブジェクトBには別の1対多の関係があります。コアデータの推奨事項のため、Bの各インスタンスがその親Aを指し、同じ親Bを指すC。

A <->> B <->> C

問題-MOCの設定

バターのようにスムーズな応答性を維持するために、3レベルのmanagedObjectContext構造を作成しました。

  1. 親MOC-を使用して独自のプライベートスレッドで実行されNSPrivateQueueConcurrencyTypepersistentStoreCoordinator
  2. MainQueue MOC-を使用してmainThreadで実行され、NSMainQueueConcurrencyType親MOC1があります
  3. 解析操作ごとに、プライベートキューと親mainQueueMOCを持つ3番目のMOCを作成します。

私のメインデータコントローラーは、MOC 2の通知にオブザーバーとして追加されるNSManagedObjectContextDidSaveため、MOC 2performBlock:がMOC1に保存するたびに、保存操作を実行するトリガーがトリガーされます(のために非同期にperformBlock:)。

問題-構文解析

大きなJSONファイルをコアデータ構造に解析するために、繰り返しパーサーを作成しました。このパーサーは、新しいMOC(3)を作成することから始まります。次に、オブジェクトAのデータを取得し、そのプロパティを解析します。次に、パーサーはBのJSONリレーションを読み取り、データで満たされた対応するオブジェクトを作成します。これらの新しいオブジェクトは、Aを呼び出すことによってAに追加されますaddBObject:。パーサーは繰り返し発生するため、Bの解析はCの解析を意味し、ここでも新しいオブジェクトが作成されてBにアタッチされます。これはすべてMOC3でperformBlock:行われます。

  • 解析(「A」オブジェクトを作成し、Bの解析を開始します)
    • Aの解析(「B」オブジェクトを作成し、それらをAにアタッチして、Cの解析を開始します)
      • Bの解析(「C」オブジェクトを作成し、それらをBにアタッチします)
        • Cの解析(データをCオブジェクトに格納するだけ)

各解析操作の後に、MOC 3を保存し、mainThreadにメインMOCの保存操作をディスパッチします(2)。通知のため、NSManagedObjectContextDidSaveMOC1は非同期で自動保存します。

        if (parsed){
            NSError *error = nil;
            if (![managedObjectContext save:&error])
                NSLog(@"Error while saving parsed data: %@", error);
        }else{
            // something went wrong, discard changes
            [managedObjectContext reset];
        }

        dispatch_async(dispatch_get_main_queue(), ^{                
            // save mainQueueManagedObjectContext
            [[HWOverallDataController sharedOverallDataController] saveMainThreadManagedObjectContext];
        });

メモリフットプリントを解放するため、そして今のところデータを解析する必要がないため、次のことを実行しています。

[a.managedObjectContext refreshObject:a mergeChanges:NO];

解析されたAIごとに。

約10個のAを解析する必要があるため、すべてが約10個のBを持ち、すべてが約10個のCを持ち、多くのmanagedObjectが生成されます。

問題-楽器

すべてが正常に動作します。唯一のことは、割り当てツールをオンにすると、リリースされていないA、B、およびCが表示されることです。私は彼らのretainCountsなどから有用な情報を取得していません。そして、私の実際の問題はより複雑なdataModelに関するものであるため、生きているオブジェクトは深刻なメモリの問題になります。誰かが私が間違っていることを理解できますか?正しいmanagedObjectを使用して他のmanagedObjectContextsでrefreshObjectsを呼び出すことも機能しません。ハードだけが機能しているresetように見えますが、UIで使用される生きているオブジェクトへのポインターを失います。

私が試した他の解決策

  • 双方向の関係ではなく、一方向の関係を作成してみました。これにより、コアデータの不整合や奇妙な動作を引き起こす他の多くの問題が発生します(オブジェクトのぶら下がりやコアデータがnn関係ではなく1-n関係を生成するなど(逆の関係が不明なため)。

  • NSManagedObjectContextDidSaveオブジェクトの通知を取得するときに、変更または挿入された各オブジェクトを更新してみました

これらの両方の「ソリューション」(ちなみに機能しません)も少しハッキーなようです。これは行く方法ではありません。ただし、メモリフットプリントを増やすことなく、UIをスムーズに保つことで、これを機能させる方法があるはずです。

-CodeDemo

http://cl.ly/133p073h2I0j

-さらなる調査

mainContext(mainSaveの後)でこれまでに使用されたすべてのオブジェクト(面倒な作業)を更新した後、オブジェクトのサイズは48バイトに縮小されます。これは、オブジェクトにすべて障害が発生しているが、メモリにポインタが残っていることを示しています。すべて障害が発生している約40.000のオブジェクトがある場合でも、メモリには1.920 MBがあり、persistentManagedObjectContextがリセットされるまで解放されません。そして、これは、managedObjectへのすべての参照を失うため、やりたくないことです。

4

3 に答える 3

5

ロビン、

私はあなたとは異なる方法で解決した同様の問題を抱えています。あなたの場合、3番目のIMO、冗長MOC、親MOCがあります。私の場合、2 つの MOC が、昔ながらの方法で、DidSave 通知を介して永続的なストア コーディネーターを介して通信できるようにします。新しいブロック指向の API により、これがよりシンプルかつ堅牢になります。これにより、子 MOC をリセットできます。3 番目の MOC からパフォーマンス上の利点が得られますが、私が活用している SQLite 行キャッシュよりも大きな利点ではありません。あなたのパスはより多くのメモリを消費します。最後に、DidSave 通知を追跡することで、作成されたアイテムをトリミングできます。

MALLOC_TINYところで、おそらく、MALLOC_SMALLVM リージョンのサイズの大幅な増加にも苦しんでいるでしょう。私のトレーリング トリミング アルゴリズムにより、アロケータはスペースをより早く再利用できるようになり、したがって、これらの問題のある領域の成長が遅くなります。私の経験では、これらのリージョンは常駐メモリのフットプリントが大きいため、私のアプリ Retweever が強制終了される主な原因です。あなたのアプリも同じ運命をたどっていると思います。

メモリの警告が表示されたら、次のスニペットを呼び出します。

[self.backgroundMOC performBlock: ^{ [self.backgroundMOC reset]; }];

[self.moc save];

[self.moc.registeredObjects trimObjects];

-[NSArray(DDGArray) trimObjects]配列を通過してオブジェクトを更新するだけで、オブジェクトがトリミングされます。

要約すると、Core Data は、多くの MOC に表示される項目に対して、書き込み時コピー アルゴリズムを実装しているようです。したがって、予期しない方法で保持されます。メモリ使用量を最小限に抑えるために、インポート後にこれらの接続を切断することに重点を置いています。私のシステムは、SQLite の行キャッシュにより、許容できるパフォーマンスを発揮しているようです。

アンドリュー

于 2012-10-31T13:10:44.690 に答える
1

NSManagedObjectContext特定の目的のために保持するすべてについて、インスタンスを蓄積しますNSManagedObject

Aは、自由にインスタンス化して保存し、変更を保存して後で破棄NSManagedObjectContextすることができる単なるメモ用紙です。NSPersistentStore

解析操作 (レイヤー 3) では、 op の MOC を作成し、解析を行い、MOC を保存してから破棄します。

強い参照で保持されているMOCのレイヤーが少なくとも1つあるように感じます。

基本的に、MOC ごとに質問します。「なぜこのオブジェクトとそれに関連付けられた子を生かしておくのか」.

于 2012-10-30T08:41:11.513 に答える
0

私は非常に似たようなことをするインポートヘルパーを持っています。

以下のコードを見て、それが役立つかどうかを確認してください

__block NSUInteger i = 0;
NSArray *jsonArray = ...
for (NSDictionary *dataStucture in jsonArray)
{
    [managedObjectContext performBlock:^{
        @autoreleasepool {
            i++;
            A *a = (A*)[self newManagedObjectOfType:@"A" inManagedObjectContext:managedObjectContext];
            [self parseData:[dataStucture objectForKey:@"a"]
                 intoObject:a
     inManagedObjectContext:managedObjectContext];

            [managedObjectContext refreshObject:a
                                   mergeChanges:YES];
            if (i > 20) // Arbitrary number here
            {
                NSError *error = nil;
                [managedObjectContext save:&error];
                [managedObjectContext reset];
            }

            [managedObjectContext refreshObject:a
                                   mergeChanges:YES];

        }
        dispatch_async(dispatch_get_main_queue(), ^{
            [self saveMainThreadManagedObjectContext];

            NSLog(@"DONE");
            // parsing is done, now you see that there are still
            // A's, B's and C's left in memory.
            // Every managedObjectContext is saved and no references are kept
            // to any A, B and C so they should be released. This is not true,
            // so a managedObjectContext is keeping a strong reference to these
            // objects.
        });
    }];
}
于 2012-10-30T10:31:27.150 に答える