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指示在一次访问持久存储期间应检索多少条记录。因此,如果(有或没有fetchBatchSize)要检索的记录数为 100,fetchBatchSize则 25 的 a 将导致 4 次前往商店。(换句话说,典型的 SQLite 存储执行了 4 条 SQL 语句。然而,这一切都发生在幕后。)

因此,代码片段

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

是多余的。无论如何,去商店的次数总是一次。

第二点:我不确定您对第二个 MOC 的设置是否有意义。我想你已经在后台线程中了。据我所知,重置 MOC 非常昂贵。如果您禁用 MOC 的撤消管理器,则实际上没有必要。至于循环,我相信您可以获取所有记录并让fetchBatchSize处理离散的“分块”。由于 Core Data 的错误行为,你@autoreleasepool在循环中可能只会带来有限的优势。

有用的@autoreleaspool是下载图像时。也许批处理这部分过程就足够了。

话虽如此,您可能不想更改(某种)有效的东西。

第三点:您根据未知的(对我们而言)谓词字符串计算记录数。是动态的吗?不确定这是否也可能不是问题的一部分。毕竟,不知道它是什么,记录的数量变化是令人惊讶的。

最后:检查是否可以不重置 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 回答