根据子对象中的可能属性快速过滤父NSManagedObjects

时间:2013-03-26 09:43:59

标签: objective-c core-data grand-central-dispatch nsmanagedobject nsset

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

排序全部完成,我有一些代码可以让我根据检索汽车的全套位置然后使用{{1}在我的currentPosition上设置Car属性}和filteredSetUsingPredicate:来检索最长时间。这个问题是它很慢,而且需要非常快(每秒都会发生)。我把它包装在valueForKeyPath:中,但由于NSManagedObjects不是线程安全的,因此导致了偶然的冻结。

我理想情况下需要一种方法来获取dispatch_asyncCar个对象的NSArray,Car.currentPosition可选地设置为最新Position(如果适用)(通过获取最后一个Position.time来确定1}}在最后10秒内如果存在的话)。然后我可以基于Car.currentPosition.position属性自己对返回的数组进行排序,并执行我需要做的其他位(即将没有currentPosition的那些位移到单独的非完成列表中)。

下面是我到目前为止的(慢)代码。我要么需要一种方法来加快速度(某种方式使用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;
    }

有一个名为currentTime的ivar,其中包含自比赛开始以来的秒数 - 这用于确定应显示哪些位置(因为位置可以提前很长时间保存)。

型号:

汽车

  • raceIdentifier(NSNumber - 用于限制赛车参加比赛)
  • 号码(NSNumber - 汽车的起始号码 - 用于在比赛尚未开始时对汽车进行分类)
  • 位置(位置对象的NSSet)

位置

  • 时间(NSNumber - 此职位更新的时间,以秒为单位)
  • 位置(NSNumber - 赛车中的位置,即第1位)

1 个答案:

答案 0 :(得分: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();
        });
    });
}

我在处理其他线程中的上下文时通常使用MagicalRecordit simplifies it 伟大的交易;叫我被宠坏了。