1

关于另一篇文章中的一个答案executeFetchRequest:循环使用是一种不好的做法吗?我在斯坦福 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 获取的传入对象)。如果结果为 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;
}
4

1 回答 1

2

我已经在您的问题中回答了核心数据: executeFetchRequest vs performFetch

这是我写的:

在循环中执行请求可能会对性能产生影响,但我不会担心这一点。在引擎盖下,Core Data 维护着一种缓存机制。每次执行请求时,如果数据不在缓存中,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..    
}

总结

两种方法都有效。通过它们,您可以检索已创建的照片或创建新照片。

这就是为什么循环是不可避免的。我错了吗?

不,因为您可以尝试遵循我的方法,但斯坦福课程中提供的方法比我发布的方法具有更好的性能。我没有进行任何性能测试,但如果您有兴趣可以自己做,并通过 Instruments 分析结果。

简单提示

Standford 代码中的一个简单更改可能是在后台执行 Core Data 操作,以防止主线程被阻塞。如果您有大量数据,这种方法可能很有用。如果数据很少,请保持原样。

于 2012-09-29T13:40:33.397 回答