关于另一篇文章中的一个answer:在循环中使用executeFetchRequest
是一种不好的做法吗?我在Stanford CS193p项目中看到了这种用法“Photomania”(点击链接下载项目)。相关代码如下:
[FlickrFetcher recentGeoreferencedPhotos]
用于从Flickr API中获取照片,这些照片发生在后台线程中。但是执行获取请求的循环发生在主线程中。
- (void)fetchFlickrDataIntoDocument:(UIManagedDocument *)document
{
dispatch_queue_t fetchQ = dispatch_queue_create("Flickr fetcher", NULL);
dispatch_async(fetchQ, ^{
NSArray *photos = [FlickrFetcher recentGeoreferencedPhotos];
// perform in the NSMOC's safe thread (main thread)
[document.managedObjectContext performBlock:^{
for (NSDictionary *flickrInfo in photos) {
// This is the method that will call executeFetchRequest
[Photo photoWithFlickrInfo:flickrInfo inManagedObjectContext:document.managedObjectContext];
}
[document saveToURL:document.fileURL forSaveOperation:UIDocumentSaveForOverwriting completionHandler:NULL];
}];
});
dispatch_release(fetchQ);
}
这是首次尝试从上下文中获取对象的工厂方法(根据从flickr API获取的传入对象)。如果result为nil,则将该对象插入上下文。
+ (Photo *)photoWithFlickrInfo:(NSDictionary *)flickrInfo
inManagedObjectContext:(NSManagedObjectContext *)context
{
Photo *photo = nil;
NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:@"Photo"];
request.predicate = [NSPredicate predicateWithFormat:@"unique = %@", [flickrInfo objectForKey:FLICKR_PHOTO_ID]];
NSSortDescriptor *sortDescriptor = [NSSortDescriptor sortDescriptorWithKey:@"title" ascending:YES];
request.sortDescriptors = [NSArray arrayWithObject:sortDescriptor];
NSError *error = nil;
NSArray *matches = [context executeFetchRequest:request error:&error];
if (!matches || ([matches count] > 1)) {
// handle error
} else if ([matches count] == 0) {
photo = [NSEntityDescription insertNewObjectForEntityForName:@"Photo" inManagedObjectContext:context];
photo.unique = [flickrInfo objectForKey:FLICKR_PHOTO_ID];
photo.title = [flickrInfo objectForKey:FLICKR_PHOTO_TITLE];
photo.subtitle = [flickrInfo valueForKeyPath:FLICKR_PHOTO_DESCRIPTION];
photo.imageURL = [[FlickrFetcher urlForPhoto:flickrInfo format:FlickrPhotoFormatLarge] absoluteString];
photo.whoTook = [Photographer photographerWithName:[flickrInfo objectForKey:FLICKR_PHOTO_OWNER] inManagedObjectContext:context];
} else {
photo = [matches lastObject];
}
return photo;
}
答案 0 :(得分:2)
我已在您的问题Core data: executeFetchRequest vs performFetch中回复。
这是我写的:
在循环中执行请求可能会影响性能 但我不担心。核心数据保持领先 一种缓存机制。每次执行请求时,如果是数据 不在缓存中,Core Data会在您的商店中执行往返 (例如sql文件)并使用它拥有的对象填充缓存 检索。如果您执行相同的查询,则往返不会 由于缓存机制再次执行。无论如何,你可以避免 在运行循环中执行请求,只需移动该请求 在循环之外。
在这种情况下,for循环中的请求是正确的,因为您需要找到当前(NSDictionary *)flickrInfo
的可能匹配项。
另一种方法是,可以将方法移出方法
+ (Photo *)photoWithFlickrInfo:(NSDictionary *)flickrInfo
inManagedObjectContext:(NSManagedObjectContext *)context;
例如,修改此方法以容纳NSArray
个结果,如:
+ (Photo *)photoWithFlickrInfo:(NSDictionary *)flickrInfo photoResults:(NSArray*)results
inManagedObjectContext:(NSManagedObjectContext *)context;
使用以下
替换第一段代码- (void)fetchFlickrDataIntoDocument:(UIManagedDocument *)document
{
dispatch_queue_t fetchQ = dispatch_queue_create("Flickr fetcher", NULL);
dispatch_async(fetchQ, ^{
NSArray *photos = [FlickrFetcher recentGeoreferencedPhotos];
// perform in the NSMOC's safe thread (main thread)
[document.managedObjectContext performBlock:^{
NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:@"Photo"];
NSArray *results = [context executeFetchRequest:request error:&error];
for (NSDictionary *flickrInfo in photos) {
// This is the method that will call executeFetchRequest
[Photo photoWithFlickrInfo:flickrInfo photoResult:results inManagedObjectContext:document.managedObjectContext];
}
[document saveToURL:document.fileURL forSaveOperation:UIDocumentSaveForOverwriting completionHandler:NULL];
}];
});
dispatch_release(fetchQ);
}
在这种情况下,通过请求您可以检索所有存储的照片。数组(托管对象)传递给+(Photo*)photoWithFlickrInfo:photoResults:inManagedObjectContext:
。
现在在+(Photo *)photoWithFlickrInfo:photoResults:inManagedObjectContext:
范围内,您需要为results
设置一个谓词,根据[flickrInfo objectForKey:FLICKR_PHOTO_ID];
找到可能的候选者。动机非常简单:您已将请求移出循环,现在您需要检索特定的请求。所以,例如,你可以这样做:
+ (Photo *)photoWithFlickrInfo:(NSDictionary *)flickrInfo photoResults:(NSArray*)results
inManagedObjectContext:(NSManagedObjectContext *)context
{
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"unique == %@", [flickrInfo objectForKey:FLICKR_PHOTO_ID]];
NSArray* filteredPredicate = [results filterUsingPredicate:predicate];
// now filteredPredicate is the same as matches in the second snippet of your code.
// do the other code here..
}
综述
两种方法都有效。通过它们,您可以检索已创建的照片或创建一个新照片。
这就是为什么循环是不可避免的。我错了吗?
不,因为你可以尝试遵循我的方法但是Standford课程中提供的方法比我发布的方法有更好的表现。我没有进行任何性能测试,但如果你有兴趣你可以自己做,并通过仪器分析结果。
简单提示
Standford代码的一个简单变化可能是在后台执行Core Data操作,防止主线程被阻塞。如果您有大量数据,这种方法可能很有用。如果数据最小,请保持原样。