7

Ok。これは、次の質問とまったく同じです:フェッチ バッチ サイズを設定するときに NSFetchedResultsController がすべての行をロードするのはなぜですか?

しかし、彼の解決策は私の解決策ではありません。

数千のレコードを含む画面があり、すべてをロードするのが遅いです。バッチ サイズを 30 (画面上のセルの約 3 倍) に設定しても、何らかの理由でループしてすべてのバッチが読み込まれます。

ここにコードがあります

- (NSFetchedResultsController *)guestCardFetchedResultsController
{
    if (guestCardFetchedResultsController != nil) {
        return guestCardFetchedResultsController;
    }

    // SELECT * from GuestCard
    NSFetchRequest* fetchRequest = [[NSFetchRequest alloc] init];
    NSEntityDescription *entity = [NSEntityDescription entityForName:@"GuestCard" inManagedObjectContext:self.context];
    [fetchRequest setEntity:entity];
    // ORDER BY updated DESC
    NSSortDescriptor* updatedSortDescriptor = [NSSortDescriptor sortDescriptorWithKey:@"created" ascending:NO];
    [fetchRequest setSortDescriptors:@[updatedSortDescriptor]];
    fetchRequest.fetchBatchSize = 30;
    NSString *cacheName = self.isReportProblemView ? @"reportProblemGuestCardsAll" : @"guestCardsAll";

    [NSFetchedResultsController deleteCacheWithName:cacheName];
    NSFetchedResultsController *aFetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:self.context sectionNameKeyPath:@"sectionIdentifier" cacheName:cacheName];
    aFetchedResultsController.delegate = self;
    self.guestCardFetchedResultsController = aFetchedResultsController;

    // Clean up

    NSError *error = nil;
    if (![[self guestCardFetchedResultsController] performFetch:&error]) {
    }

    return self.guestCardFetchedResultsController;
}

このシナリオでは、特に面白いことはしていません。デリゲート コードの一部を次に示します (セルの作成は除きます。これは、画面上のセルの数に対してのみ呼び出されることを確認しました)。

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
    if ([self.guestCardFetchedResultsController.fetchedObjects count] == 0) {
        return 1;
    }
    // Return the number of sections.
    return [[self.guestCardFetchedResultsController sections] count];
}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
    if ([self.guestCardFetchedResultsController.fetchedObjects count] == 0) {
        return 1;
    }
    // Return the number of rows in the section.
    id <NSFetchedResultsSectionInfo> sectionInfo = [guestCardFetchedResultsController sections][section];
    return [sectionInfo numberOfObjects];
}

- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section
{
    if ([self.guestCardFetchedResultsController.fetchedObjects count] == 0) {
        return @"";
    }

    return [[self.guestCardFetchedResultsController sections][section] name];
}
4

3 に答える 3

8

NSFetchedResultsController initializerのドキュメントを読んだ後、問題は、セクション キー パス (sectionIdentifier) と同じように自然に並べ替えられないフェッチ要求 (作成された) に並べ替えがあることだと思います。私が見ているドキュメントの特定の文は次のように述べています。

このキー パスが fetchRequest の最初の並べ替え記述子によって指定されたものと同じでない場合、それらは同じ相対順序付けを生成する必要があります。

最初に sectionIdentifier でソートしてから作成するようにフェッチ要求を変更することをお勧めします。これで問題は解決すると思います。

 NSSortDescriptor* updatedSortDescriptor = [NSSortDescriptor sortDescriptorWithKey:@"created" ascending:NO];
 NSSortDescriptor* sectionSortDescriptor = [NSSortDescriptor sortDescriptorWithKey:@"sectionIdentifier" ascending:NO];
 // It's critical the sectionSortDescriptor is first
 [fetchRequest setSortDescriptors:@[sectionSortDescriptor, updatedSortDescriptor]];

createdまたはsectionIdentifierプロパティのいずれかが実際にエンティティ クラスのメソッドである場合、最初に各エンティティでそのメソッドを実行する必要があるため、並べ替え/セクション化する前に Core Data がすべてのデータをロードすることを確実に強制することに注意してください。

于 2013-10-19T17:28:09.117 に答える
5

fetchBatchSizeNSArray でオブジェクトを返すのではなく使用すると、特別なバッチ フォルト配列が返されます。すべてのオブジェクト ID をロードしますが、配列内のすべてのオブジェクトはフォルトです。つまり、それらのデータはロードされませんでした。つまり、プレースホルダー オブジェクトと同様です。次に、オブジェクトのプロパティにアクセスするときに配列をループする (またはテーブルのリロードを行う) と、fetchBatchSize までの次のオブジェクトを含むそのオブジェクトのプロパティのデータベースクエリが実行されます (ちなみに最小値は 4 のようです)。クエリを確認してこの内部動作をデバッグする最善の方法は、スキームを編集して run 引数を追加することです。

-com.apple.CoreData.SQLDebug 1

これは、さまざまなクエリをコンソールに出力します。行 ID の最初のクエリが表示され、次にデータの各バッチ ロードが表示されます。

そのため、すべての行 ID をロードして障害のあるオブジェクトを作成するのが遅いという経験があるかもしれませんが、それは速いはずです。

もう 1 つの可能性は、sectionIdentifier プロパティにアクセスしてセクションを構築する場合です。これにより、オブジェクトも読み込まれ、すべてのバッチが読み込まれます。それを解決するために、sectionIdentifier を含めるように propertiesToFetch を設定してみることができます。これにより、最初のクエリがそのプロパティに読み込まれ、アクセスされたときにオブジェクトが読み込まれなくなります。

于 2015-12-11T14:36:59.747 に答える
-1

私は同じ質問をしましたが、最終的には正常に機能すると結論付けました。

fetchBatchSize を設定すると、fetchRequest はすべてのデータをキャッシュにロードします。そうしないと、このパラメーターは役に立たないからです。ロード直後のデータを利用するために使われているのでしょう。しかし、[fetchResultController objectAtIndexPath:indexPath]fetchRequest ではなく、ManagedObjectContext (私が推測する) によって満たされるオブジェクトを取得する場合。

したがって、fetchBatchSize を 0 に設定すると、フォールトにアクセスするたびに永続ストアから要求されます。または、他の値を設定し、テーブル ビューをスクロールするたびに fetchLimit と fetchOffset を設定することもできます。ただし、全体のオブジェクト数を知る必要があります。

于 2013-10-18T21:26:18.143 に答える