0

いくつかのコア データ エントリを選択して並べ替えています。これは簡単ですが、距離による並べ替えの計算のため、別のスレッドに入れたいと思いました。#ifdef THREADS以下のセクション内にコードを追加して、他のスレッド用に新しい NSManagedObjectController を作成するという通常の方法でそれを行いました。

これで、ユーザー インターフェイスを遅くすることなく、フェッチ、並べ替え、整理を行うことができ、準備ができたらテーブルが更新されます。

しかし、私はローカル管理対象オブジェクト コンテキストでフェッチを行っているため、フェッチしたオブジェクトはメイン スレッドでは有効ではありません。実際、それらはテーブル ビュー セルから消えてしまいます。

ですから、私の質問は次のとおりです。1) メインのマネージド オブジェクト コンテキストを使用しても問題ないでしょうか。ローカル コンテキストをまったく作成せず、読み取りのみを行っているからです。2) 並べ替えと整理の後、検索管理オブジェクト コンテキスト オブジェクトの ObjectID 値を使用して、メイン スレッドのメイン管理オブジェクト コンテキストからすべてのオブジェクトを調べて再取得する必要がありますか? 3) メインのマネージド オブジェクト コンテキストでフェッチを行う必要がありPerformBlockAndWaitますが、スレッド上で返されたオブジェクトの並べ替えと整理を維持する必要がありsearchQますか? 4) 他に考えたことがないことはありますか?

- (void) fetchShopLocations {
#ifdef THREADS
    dispatch_queue_t searchQ = dispatch_queue_create( "searchQueue", NULL );
    dispatch_async( searchQ, ^{
#endif
        NSError *error;

#ifdef THREADS
        // Make a managed object context that can be not on the main thread
        NSManagedObjectContext *localContext = [[NSManagedObjectContext alloc] init];
        [localContext setPersistentStoreCoordinator: [self.managedObjectContext persistentStoreCoordinator]];
#else
        NSManagedObjectContext *localContext = self.managedObjectContext;
#endif

        // Get the array of locations sorted by distance
        NSArray *locations = NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName: NSStringFromClass([Shop class])];
        // No sort descriptors since I cannot sort on things not in the database

        NSArray *locations = [context executeFetchRequest: request error: &error];
        if( !locations ) {
            NSLog( @"Shop fetch error: %@", error );
            return;
        }
        locations = [locations sortedArrayUsingComparator: ^NSComparisonResult( Shop *loc1, Shop *loc2 ) {
                    double dist1 = [loc1 distanceFromLatitude: self.referenceLatitude andLongitude: self.referenceLongitude];
                    double dist2 = [loc2 distanceFromLatitude: self.referenceLatitude andLongitude: self.referenceLongitude];
                    if( dist1 < dist2 )
                            return NSOrderedAscending;
                    else if( dist1 > dist2 )
                            return NSOrderedDescending;
                    return [[loc1 name] localizedCaseInsensitiveCompare: [loc2 name]];
        }];

        NSMutableArray *sections = [NSMutableArray arrayWithCapacity: 5];
        NSMutableDictionary *section;
        NSMutableArray *rows;

        section = [NSMutableDictionary dictionaryWithCapacity: 2];
        rows = [NSMutableArray arrayWithCapacity: locations.count];
        __block double sectionLimitUserUnits = 5.0;
        __block double sectionLimitKilometers;
        if( self.userUnits == LocationDistanceUnitsMiles )
              sectionLimitKilometers = sectionLimitUserUnits * KILOMETERS_PER_MILE;
        else  sectionLimitKilometers = sectionLimitUserUnits;
        [section setValue: [NSString stringWithFormat: @"Within %g", sectionLimitUserUnits] forKey: kSectionHeaderTitleKey];
        [locations enumerateObjectsUsingBlock: ^( Shop *loc, NSUInteger idx, BOOL *stop ) {
            double distance = [loc distanceFromLatitude: self.referenceLatitude andLongitude: self.referenceLongitude];
            if( distance > self.maxDistance ) {
                *stop = YES;
            } else {
                while( distance > sectionLimitKilometers ) {
                    [section setValue: [rows copy] forKey: kRowKey];
                    [sections addObject: [section copy]];
                    [section removeAllObjects];
                    [rows removeAllObjects];

                    sectionLimitUserUnits += 5.0;
                    if( self.userUnits == LocationDistanceUnitsMiles )
                          sectionLimitKilometers = sectionLimitUserUnits * KILOMETERS_PER_MILE;
                    else  sectionLimitKilometers = sectionLimitUserUnits;
                    [section setValue: [NSString stringWithFormat: @"Within %g", sectionLimitUserUnits] forKey: kSectionHeaderTitleKey];
                }
                [rows addObject: loc];
            }
        }];
        [section setValue: [rows copy] forKey: kRowKey];
        [sections addObject: [section copy]];

#ifdef THREADS
        dispatch_async( dispatch_get_main_queue(), ^{
#endif
            self.sections = sections;
            [self.tableView reloadData];
#ifdef THREADS
        });
    });
    dispatch_release( searchQ );
#endif
}

スレッド競合の非決定論的な性質のため、シミュレーターで動作するように見えるものをテストするだけでは当てにならないことを私は知っています。

前もって感謝します!

4

2 に答える 2

0

NSManagedObjectContext に対するすべての操作を単一のスレッドまたはシリアル キュー内に保持することを強くお勧めします。iOS5 では、プライベート キュー同時実行タイプで NSManagedObjectContext を作成し、そのコンテキストの performBlock メソッドですべての操作を処理できます。したがって、質問 1 に答えるには、NSManagedObjectContext とそれに関連付けられたスレッドまたはキュー内の関連付けにのみアクセスしてください。別のもののために作成された場合は、メイン キューでアクセスしないでください。途中で頭が痛くなる!

質問 2 が最善の方法です。バックグラウンドで面倒な作業を行い、必要なものだけをメイン スレッドでフェッチすることは、実行しようとしている処理を安全に処理する優れた方法です。

質問 3: メイン マネージド オブジェクトが NSMainQueueConcurrencyType コンカレンシー タイプで作成された場合、メイン スレッドで performBlock: または performBlockAndWait: を実行する必要はありません。バックグラウンド スレッドから何かを実行する必要がある場合にのみ、これらを使用する必要があります。

また、MagicalRecordを強くお勧めします。これにより、コア データを扱う作業がずっと楽になり、単純なことを行うために書かなければならなかったボイラープレート コードの量が大幅に減りました。

〜ジェームズ

于 2013-08-24T03:53:15.447 に答える