5

NSFetchResultsControllerを使用して、UITableViewに100,000以上のレコードを表示しています。これは機能しますが、特にiPad 1では低速です。ロードに7秒かかる場合があり、これはユーザーにとって苦痛です。

セクションも使用できるようにしたいのですが、これにより、ロード時間に少なくともさらに3秒が追加されます。

これが私のNSFetchResultsControllerです。

- (NSFetchedResultsController *)fetchedResultsController {

    if (self.clientsController != nil) {
        return self.clientsController;
    }

    NSFetchRequest *request = [[NSFetchRequest alloc] init];
    NSEntityDescription *entity = [NSEntityDescription entityForName:@"Client" inManagedObjectContext:self.managedObjectContext];
    [request setEntity:entity];
    [request setPredicate:[NSPredicate predicateWithFormat:@"ManufacturerID==%@", self.manufacturerID]];
    [request setFetchBatchSize:25];

    NSSortDescriptor *sort = [[NSSortDescriptor alloc]  initWithKey:@"UDF1" ascending:YES];
    NSSortDescriptor  *sort2= [[NSSortDescriptor alloc] initWithKey:@"Name" ascending:YES];
    [request setSortDescriptors:[NSArray arrayWithObjects:sort, sort2,nil]];

    NSArray *propertiesToFetch = [[NSArray alloc] initWithObjects:@"Name", @"ManufacturerID",@"CustomerNumber",@"City", @"StateProvince",@"PostalCode",@"UDF1",@"UDF2", nil];
    [request setPropertiesToFetch:propertiesToFetch];

    self.clientsController =
    [[NSFetchedResultsController alloc] initWithFetchRequest:request
                                        managedObjectContext:self.managedObjectContext sectionNameKeyPath:nil
                                                   cacheName:nil];

    return self.clientsController;

}

NSPredicateで使用されているManufacturerIDのインデックスがあります。これはかなり基本的なNSFetchRequestのようです-これを高速化するために私ができることは何ですか?それとも、制限に達したばかりですか?私は何かが欠けているに違いありません。

4

2 に答える 2

7

まず、NSFetchedResultsControllerのキャッシュを使用して、最初のフェッチ後の表示を高速化できます。これはすぐにほんの一瞬になります。

2番目:最初の画面のみを表示してから、残りをバックグラウンドでフェッチすることができます。私はこれを次のように行います:

  • ビューが表示されたら、最初のページのキャッシュがあるかどうかを確認します。
  • そうでない場合は、最初のページをフェッチします。これは、フェッチ要求のを設定することで実現できますfetchLimit
    • セクションを使用している場合は、2つのクイックフェッチを実行して、最初のセクションヘッダーとレコードを判別します。
  • 2番目にフェッチされた結果コントローラーにバックグラウンドスレッドでの長いフェッチを設定します。
    • 子コンテキストを作成して使用するperformBlock:か、
    • を使用しますdispatch_async()
  • 2番目のFRCをテーブルビューに割り当て、を呼び出しますreloadData

これは、20万件を超えるレコードを持つ最近のプロジェクトの1つで非常にうまく機能しました。

于 2013-02-05T15:00:42.307 に答える
0

@Mundiが提供した答えが受け入れられることは知っていますが、それを実装しようとして問題が発生しました。具体的には、2番目のFRCによって作成されたオブジェクトは、他のスレッドのManagedObjectContextに基づいています。これらのオブジェクトはスレッドセーフではなく、他のスレッドの独自のMOCに属しているため、私が見つけた解決策は、オブジェクトがロードされているときにオブジェクトに障害を発生させることでした。したがって、cellForRowAtIndexPathに次の行を追加しました。

NSManagedObject *object = [self.fetchedResultsController objectAtIndexPath:indexPath];
object = (TapCellar *)[self.managedObjectContext existingObjectWithID:[object objectID] error:nil];

次に、現在の正しいスレッドのオブジェクトがあります。もう1つの注意点は、オブジェクトに加えた変更がバックグラウンドMOCに反映されないため、それらを調整する必要があることです。私がしたことは、バックグラウンドMOCをプライベートキューMOCにし、フォアグラウンドMOCを次のようにその子にすることでした。

NSPersistentStoreCoordinator *coordinator = [self persistentStoreCoordinator];
if (coordinator != nil) {
  _privateManagedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
  [_privateManagedObjectContext setPersistentStoreCoordinator:coordinator];

  _managedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];
  [_managedObjectContext setParentContext:_privateManagedObjectContext];

}

これで、メインスレッドに変更を加えるときに、次のようにして簡単に調整できます。

if ([self.managedObjectContext hasChanges]) {
   [self.managedObjectContext performBlockAndWait:^{
      NSError *error = nil;
      ZAssert([self.managedObjectContext save:&error], @"Error saving MOC: %@\n%@",
             [error localizedDescription], [error userInfo]);
   }];
}

この時点でテーブルデータをリロードするので、返されるのを待ちますが、必要に応じて待たないように選択できます。通常は1つか2つしか変更されないため、30K以上のレコードでもプロセスは非常に高速です。

これがこれで立ち往生している人に役立つことを願っています!

于 2014-06-17T14:01:48.510 に答える