我正在尝试以特定大小(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张图片后,程序会一直崩溃。
答案 0 :(得分:3)
第一点:也许对fetchLimit
和fetchBatchSize
做什么有一些基本的误解。
fetchLimit
和fetchOffset
确定提取的记录数和记录数。
fetchBatchSize
表示在一次访问持久性存储期间应检索的记录数。因此,如果(有或没有fetchBatchSize
)将要检索的记录数为100,则fetchBatchSize
为25将导致4次到商店的旅行。 (换句话说,4个典型的SQLite存储执行的SQL语句。但是,这一切都发生在幕后。)
因此,代码片段
request.fetchLimit = x;
request.fetchBatchSize = x;
是多余的。无论如何,到商店的旅行总数将是一个。
第二点:我不确定你对第二个MOC的设置有多大意义。我想你已经在后台线程了。据我所知,重置MOC非常昂贵。如果禁用MOC的撤消管理器,则没有必要。至于循环,我相信你可以只获取所有记录并让fetchBatchSize
处理离散的“分块”。由于Core Data的错误行为,循环中的@autoreleasepool
可能只会带来有限的优势。
@autoreleaspool
有用的地方是下载图像的时间。也许批处理这部分过程就足够了。
话虽这么说,你可能不想改变那种(有点)工作的东西。
第三点:您根据未知(对我们)谓词字符串计算记录数。它是动态的吗?不确定这可能也不是问题的一部分。毕竟,不知道它是什么,令人惊讶的是记录的数量发生了变化。
最后:检查你是否可以不重置你的MOC。
答案 1 :(得分:0)
问题在于谓词。它会在没有图像的情况下获取所有产品。如果我下载图像,谓词的结果集会在后续提取时发生变化,并且每次都会变小。解决方案是以相反的顺序处理结果集。所以改变:
for (NSUInteger offset = 0; offset < numberOfProducts; offset += batchSize)
分为:
for (NSInteger offset = MAX(numberOfProducts - batchSize, 0); offset > 0; offset -= batchSize)