1

私のアプリケーションでは、ストレージに CoreData を使用し、NSFetchedResultsController を使用してデータを表示しています。

私は raywenderlich のチュートリアルに従ってそれを完成させました。それは大量のコードですが、一般的には適切に機能しています。必要に応じてその一部を投稿します。私は理解できない問題に固執しました。

NSFetchedResultsController と組み合わせた UITableView 内に表示されるデータは、バックグラウンドで更新できます-ここで私の問題が始まりました。

Pull-to-refresh アプローチを行っており、別のスレッドでバックグラウンドでダウンロードを開始しています。このスレッド用に作成された独自の NSManagedObjectContext を使用し、すべてのオブジェクトが作成された後に保存します。

コードは次のとおりです。

- (void)refresh:(id)sender
{
[ServerConnection downloadContactsForToken:token success:^(NSDictionary* responseObject)
    {
        dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
            NSManagedObjectContext* context = [[[AppManager sharedAppManager] createManagedObjectContext] retain];

            NSArray* responseContacts = responseObject[@"contacts"];
            [responseContacts enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
                //ContactDB is NSManagedObject
                [[[ContactDB alloc] initWithDictionary:obj andContext:context] autorelease];
            }];

            [context save:nil];
            [context release];

            dispatch_async(dispatch_get_main_queue(), ^{
                [self.refreshControl endRefreshing];
            });
        });
    }];
}

Apple docs で読んだことによると、メインスレッド NSManagedObjectContext でこれらの変更を検出する適切な方法は次のとおりです。

- (void) viewDidAppear:(BOOL)animated
{
    [super viewDidAppear:animated];

    [[NSNotificationCenter defaultCenter] addObserver:self
                                         selector:@selector(managedObjectContextDidSave:)
                                             name:NSManagedObjectContextDidSaveNotification
                                           object:nil];
}

- (void)managedObjectContextDidSave:(NSNotification *)notification {
    dispatch_async(dispatch_get_main_queue(), ^{
        if(notification.object != [[AppManager sharedAppManager] managedObjectContext]) {
            [[[AppManager sharedAppManager] managedObjectContext] mergeChangesFromContextDidSaveNotification:notification];
        }
    });
}

基本的に、managedObjectContext の変更に関する通知を受け取ると、変更をメイン スレッド コンテキストにマージします。そして、それは一般的に機能しますが、プロファイリングを開始した後、記述されたプロセスにマージされたすべてのオブジェクトが決して割り当て解除されないことを発見しました.

メインスレッドですべてを行うと、動作します-UITableViewをスクロールすると、期待どおりに割り当てが解除されます。

回避策を見つけたので、電話しています:

[[[AppManager sharedAppManager] managedObjectContext] reset];

マージが完了したら:

[[[AppManager sharedAppManager] managedObjectContext] mergeChangesFromContextDidSaveNotification:notification];
[[[AppManager sharedAppManager] managedObjectContext] reset];

しかし、なぜそれをしなければならないのか、それが他の何かを壊すのかどうかはわかりません. バックグラウンドでデータを更新するより良い方法があるかもしれませんが、私は完全に間違った方向に進んでいます。

4

2 に答える 2

4

通常、Foundation の通常のメモリ管理規則 (参照カウント) を除いて、マネージド オブジェクトに対して特別なことを行うべきではありません。したがって、必要のないときにどこにも保持しないようにしてください。

を使用してオブジェクト バンクをフォールトに変換-refreshObject:mergeChanges:する必要があるのは、オブジェクト グラフを部分的にトリミングする必要があり、オブジェクトへの強力な参照が残っている場合のみです。

そして、私はあなたのコードで他のいくつかのことに気付きました.

すべての context-did-save 通知を購読しています。所有していないコンテキストの通知を受け取る可能性があるため、これは危険です。たとえば、アドレス帳から、または使用しているサードパーティのライブラリから。

ネットワーク操作完了ハンドラーでは、作業をグローバル同時キューにディスパッチし、そこから新しいコンテキストを作成します。グローバル同時キューを使用することで、同時タスクの数を制御していません。a) スレッドが不足し、b) 1 つの永続ストア コーディネーターと 1 つの永続ストアをめぐって競合する多くの新しいコンテキストが作成される可能性があります。シリアル キューにディスパッチするか、プライベート シリアル キューを管理するプライベート キュー同時実行タイプのコンテキストを使用することをお勧めします。

于 2014-01-22T18:19:27.610 に答える