2

かなり複雑なフィルタリングが必要なプロジェクトに取り組んでいます。Race多数のCarオブジェクトが接続されたNSManagedObject があります。Carには複数のPositionオブジェクトを含めることができます。特定のレースについて、すべての車のリストを取得する必要がありますが、レースが開催されているかどうかに基づいて異なる順序で並べる必要があります。Car.numberレースが開始されていない場合は、 (単純)で注文する必要があります。レースが行われている場合は、現在の位置で並べ替える必要があります。難しいのは、検索された位置がセット内の最新の位置ではなく、特定の範囲内の最新の位置であり、位置がまったく存在しない可能性があることです (つまり、車がレースから外れている場合)。

並べ替えがすべて完了し、車の位置の完全なセットを取得し、およびを使用して最大時間を取得することに基づいて、currentPositionプロパティを設定できるコードが動作しています。これに関する問題は、非常に遅く、非常に高速である必要があることです (1 秒ごとに発生するため)。私はそれをラップしましたが、NSManagedObjects がスレッドセーフではないため、時折フリーズが発生しました。CarfilteredSetUsingPredicate:valueForKeyPath:dispatch_async

私が理想的に必要としているのは、必要に応じて最新に設定されたCarオブジェクトの NSArray を取得する方法です (存在する場合は、過去 10 秒以内に最後を取得することによって決定されます)。次に、返された配列をプロパティに基づいて自分で並べ替え、必要な他のビットを実行できます (つまり、 currentPosition のないものを非フィニッシャーの別のリストに移動します)。Car.currentPositionPositionPosition.timeCar.currentPosition.position

以下は、これまでの(遅い)コードです。これを高速化する方法 (スレッドの問題を引き起こさずに dispatch_async を使用する何らかの方法) か、CoreData からの初期フェッチを行うためのより良い方法が必要です。

現在のコード:

    - (void)performFetchWithCallback:(void (^)(void))completionBlock
    {
        NSError *error;
        [self.fetchedResultsController performFetch:&error];
        if (error) {
            NSLog(@"ERROR: %@", error);
        }

        NSMutableArray *cars = [NSMutableArray array];

        BOOL isRacing = NO;
        for (Car *car in _fetchedResultsController.fetchedObjects) {
            Position *position = nil;
            NSSet *carPositions = [[car valueForKey:@"positions"] copy];
            if (carPositions && currentTime > 0) {
                NSSet *positions = [carPositions filteredSetUsingPredicate:[NSPredicate predicateWithFormat:@"time < %@ && time > %@", @(currentTime), @(currentTime-10)]];
                if (positions.count > 0) {
                    NSNumber *time = [positions valueForKeyPath:@"@max.time"];
                    for (Position *p in positions) {
                        if ([p.time isEqual:time]) {
                            position = p;
                            isRacing = YES;
                        }
                    }
                }
            }
            car.currentPosition = position;
            [cars addObject:car];
        }

        self.allCars = cars;

        if (completionBlock) {
            completionBlock();
        }
    }

    - (NSFetchedResultsController *)fetchedResultsController
    {
        if (_fetchedResultsController != nil) {
            return _fetchedResultsController;
        }

        NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
        NSEntityDescription *entity = [NSEntityDescription entityForName:@"Car" inManagedObjectContext:self.managedObjectContext];
        [fetchRequest setEntity:entity];
        NSSortDescriptor *sort = [[NSSortDescriptor alloc] initWithKey:@"number" ascending:YES];
        NSPredicate *predicate = [NSPredicate predicateWithFormat:@"raceIdentifier = %@", _race.identifier];
        [fetchRequest setPredicate:predicate];
        [fetchRequest setSortDescriptors:[NSArray arrayWithObject:sort]];
        [fetchRequest setFetchBatchSize:40];

        NSFetchedResultsController *theFetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:_managedObjectContext sectionNameKeyPath:nil cacheName:@"Racing"];
        self.fetchedResultsController = theFetchedResultsController;

        return _fetchedResultsController;
    }

レースが開始されてからの秒数を含むivar という名前currentTimeがあります。これは、どのポジションを表示するかを決定するために使用されます (ポジションはかなり前に保存できるため)。

モデル:

  • raceIdentifier (NSNumber - 車をレースに限定するために使用)
  • number (NSNumber - 車の開始番号 - レースが開始されていないときに車を並べ替えるために使用されます)
  • 位置 (位置オブジェクトの NSSet)

位置

  • time (NSNumber - この位置更新の秒単位の時間)
  • 位置 (NSNumber - レースでの車の位置、つまり 1 位)
4

1 に答える 1

0

単純化しすぎて申し訳ありませんが、一言で言えば、これは機能するはずです(機能する可能性があります)。

- (void)performFetchWithCallback:(void (^)(void))completionBlock
{
    // Use a dictionary instead; you'll want to set instance variables on your cars in the main
    // thread
    __block NSMutableDictionary *carDict = [NSMutableDictionary dictionary];
    dispatch_queue_t main = dispatch_get_main_queue();
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH,NULL), ^{
        // Perform a non-resultsController fetch with a background context, using the 
        // car, number, race identifier NSFetchRequest below

        // Then your for (car in fetchedResults) computation loop, but use a
        // mutable dictionary (carDict) instead of an array, and store car IDs as
        // your keys, and their current position as their values

        dispatch_async(main, ^{
            // Here, fetch your cars into your fetched results controller based on a
            // predicate of the carDict keys you stored (id IN %@), then do a quick
            // for (car in fetchedResults) loop and assign those currentPosition values here
            if (completionBlock) completionBlock();
        });
    });
}

私は通常、他のスレッドでコンテキストを処理するときにMagicalRecord使用します。私を甘やかされて呼んでください。

于 2013-03-26T14:34:34.890 に答える