2

我正在做一个需要一些相当复杂的过滤的项目。我有一个RaceNSManagedObjectCar附加了许多对象。Car可以有多个Position对象。对于给定的比赛,我需要获取所有汽车的列表,但需要根据比赛是否进行而对它们进行不同的排序。如果比赛还没有开始,他们需要由Car.number(简单)订购。如果比赛正在进行,则需要按当前位置排序。困难在于检索到的位置不能是集合中最新的,而是特定范围内的最新的,并且可能根本没有位置(即如果赛车出局)。

排序全部完成,我有一些代码工作,允许我根据检索汽车的完整位置集,然后使用和检索最大时间来设置我的currentPosition属性。这样做的问题是它非常慢,而且需要非常快(因为它每秒都会发生)。我将它包裹在其中,但由于 NSManagedObjects 不是线程安全的,因此导致偶尔冻结。CarfilteredSetUsingPredicate:valueForKeyPath:dispatch_async

理想情况下,我需要一种方法来获取对象的 NSArray,如果适用,这些Car对象可以Car.currentPosition选择设置为最新的(如果存在,则通过在最后 10 秒内获取最后一个来确定)。然后,我可以自己根据属性对返回的数组进行排序,并执行我需要做的其他部分(即将那些没有 currentPosition 的部分移动到单独的非完成者列表中)。PositionPosition.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 - 用于将 Car 限制为 Race)
  • number (NSNumber - 赛车的起始编号 - 用于在比赛尚未开始时对赛车进行排序)
  • 位置(位置对象的 NSSet)

位置

  • time (NSNumber - 此位置更新的时间,以秒为单位)
  • 位置(NSNumber - 赛车在比赛中的位置,即第一名)
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 回答