0

特定のサイズ(batchSize)のチャンクで多くのオブジェクトを処理しようとしています。このループは機能しているように見えますが、処理するレコードは半分だけです。関連するコードは次のとおりです。

{
//Prepare fetching products without images in the database
NSFetchRequest * productFetchRequest = [NSFetchRequest fetchRequestWithEntityName:@"Product"];

//Sort by last changed photo first
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"photoModificationDate" ascending:NO];
[productFetchRequest setSortDescriptors:@[sortDescriptor]];

NSPredicate *predicate = [NSPredicate predicateWithFormat: predicateString];
[productFetchRequest setPredicate:predicate];

//First get the total count
NSUInteger numberOfProducts = [self.backgroundMOC countForFetchRequest: productFetchRequest error: &error];
NSLog(@"Getting images for: %d products", numberOfProducts);

//Then set the batchsize to get chunks of data
NSUInteger batchSize = 25;
[productFetchRequest setFetchBatchSize: batchSize];
[productFetchRequest setFetchLimit:batchSize];

//Fetch the products in batches
for (NSUInteger offset = 0; offset < numberOfProducts; offset += batchSize) {
    @autoreleasepool {
        [productFetchRequest setFetchOffset: offset];
        NSArray * products = [self.backgroundMOC executeFetchRequest:productFetchRequest error:&error];
        NSLog(@"Offset: %d, number of products: %d", offset, [products count]);
        if (!products) {
            return NO;
        }

        for (Product * product in products) {
            NSLog(@"Downloading photo for product: %@", product.number);
            [self downLoadAndStoreImageForProduct:product];
        }
        [self saveAndResetBackgroundMOC];
    }
}

return YES;

}

ログは、カウントの前半(numberOfProducts)で、期待どおりに機能することを示しています。したがって、25個の製品のチャンクが処理されます。その前半の後、ループ内のfetchrequestのレコード数は0になります。同じコードを再試行すると、(残りの)レコードの半分だけが処理されるため、合計で3/4になります。私は何が間違っているのですか?managedObjectContextは保存されるだけでなく、保存後にリセットされてメモリを節約することに注意してください。これをチャンクで行わないと、約3000枚の写真をダウンロードした後、プログラムが一貫してクラッシュします。

4

2 に答える 2

3

最初のポイント:多分、何をすべきかについていくつかの基本的な誤解がfetchLimitありfetchBatchSizeます。

fetchLimitfetchOffsetフェッチされるレコードとその数を決定します。

fetchBatchSize永続ストアへの1回のトリップ中に取得する必要があるレコードの数を示します。したがって、fetchBatchSize取得されるレコードの数が(の有無にかかわらず)100である場合fetchBatchSize、25のaは、ストアへの4回のトリップになります。(言い換えると、典型的なSQLiteストアに対して4つの実行されたSQLステートメント。ただし、これはすべて舞台裏で行われます。)

したがって、コードスニペット

request.fetchLimit      = x; 
request.fetchBatchSize  = x;

冗長です。とにかく店への旅行の数は常に1つになります。

2番目のポイント:2番目のMOCを使用したセットアップが適切かどうかはわかりません。あなたはすでにバックグラウンドスレッドにいると思います。私の知る限り、MOCのリセットにはかなりの費用がかかります。MOCのundomanagerを無効にする場合は、実際には必要ありません。ループに関しては、すべてのレコードをフェッチしてfetchBatchSize、個別の「チャンキング」を処理できると思います。Core Dataの障害動作のため@autoreleasepool、ループ内では限られた利点しかもたらされない可能性があります。

便利なの@autoreleaspoolは、画像をダウンロードするときです。おそらく、プロセスのこの部分をバッチ処理するだけで十分です。

そうは言っても、(ある種の)機能しているものを変更したくない場合があります。

3番目のポイント:未知の(私たちにとって)述語文字列に基づいてレコード数を計算します。ダイナミックですか?これも問題の一部ではないかどうかはわかりません。結局のところ、それが何であるかを知らずに、レコードの数が変化するのは驚くべきことです。

最後に、MOCをリセットせずに実行できるかどうかを確認します。

于 2013-03-26T21:37:15.860 に答える
0

問題は述語にあります。画像なしですべての商品を取得します。画像をダウンロードすると、述語の結果セットは後続のフェッチで変更され、毎回小さくなります。解決策は、結果セットを逆の順序で処理することです。だから変更:

for (NSUInteger offset = 0; offset < numberOfProducts; offset += batchSize)

の中へ:

for (NSInteger offset = MAX(numberOfProducts - batchSize, 0); offset > 0; offset -= batchSize)
于 2013-03-27T08:47:17.363 に答える