1

別の投稿の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

私はすでにあなたの質問に答えましたCore data: 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 で結果を分析できます。

簡単なヒント

スタンフォード コードの簡単な変更は、コア データ操作をバックグラウンドで実行し、メイン スレッドがブロックされるのを防ぐことです。このアプローチは、大量のデータがある場合に役立ちます。データが最小限の場合はそのままにしておきます。

于 2012-09-29T13:40:33.397 に答える