根据子对象中的可能属性快速筛选父项NSManagedObjects

问题描述:

我正在处理需要一些相当复杂的筛选的项目。我有一个Race NSManagedObject具有附加数量的Car对象。 Car可以有多个Position对象。对于特定的比赛,我需要获得所有赛车的列表,但是根据比赛是否正在进行,他们需要按不同的顺序排列。如果比赛尚未开始,他们需要订购Car.number(简单)。如果比赛正在进行,他们需要按照当前位置排序。困难在于,检索的位置不能是该组中的最新位置,而是最近在特定范围内并且可能根本没有位置(即,如果赛车不在比赛中)。根据子对象中的可能属性快速筛选父项NSManagedObjects

的排序是所有做,我有一些代码的工作,让我根据获取的全套位置为汽车,然后用filteredSetUsingPredicate:valueForKeyPath:检索最大时间对我Car设置currentPosition属性。与此相关的问题是,它非常缓慢,而且这需要很快(因为它每秒都会发生)。我把它封装在dispatch_async内,但是由于NSManagedObjects不是线程安全的,导致偶尔死机。

我非常需要的是一种方式来获得那些Car.currentPosition可选如果适用(这是由获得最后Position.time最后10秒,如果它存在内确定)设置为最新的PositionCar对象的一个​​NSArray。然后,我可以根据属性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其中包含的秒数,因为比赛开始伊娃 - 这是用来确定位置应该显示什么(如位置可以保存很长一段时间提前)。

型号:

汽车

  • raceIdentifier(NSNumber的 - 用于汽车限制在一个种族)
  • 号(NSNumber的 - 开始编号的车 - 这是用来排序赛车尚未开赛时)
  • 职位(NSSet职位对象)

位置

  • 时间(NSNumber的 - 的时间,以秒,这个位置更新)
  • 位置(的NSNumber - 汽车的在比赛中的位置即第一个)
+0

你有自己的两难境地!当你试图将你的提取/计算放在后台线程中时,你是否尝试过并且已经成功地在后台线程中保存排序后的车辆ID,将该组ID通过回调传递给主线程,然后重新获取基于这些ID的主线程中的“NSManagedObjects”?在线程抓取问题中,传递'NSIntegers'已经保存了我的培根, – 2013-03-26 12:17:09

+0

我在'dispatch_async()'中执行'performFetch:'后将所有内容都包装好,然后在运行'completionBlock()'时为主线程执行'dispatch_async' 。我没有尝试传递ID,因为我不确定这是否有助于此实例 - 我需要基于Car.Position.time进行排序,因此我需要后台线程中的整个对象除非我误解? – 2013-03-26 12:23:40

+0

我会捡起一些代码并将其作为答案发布 - 我的代码更好。 – 2013-03-26 12:29:33

道歉可能是过分简单化,但简而言之,这是应该(可以)工作。

- (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与其他线程的上下文处理的时候,作为一个it simplifies it很大交易工作;叫我宠坏了。

+0

因此,在'async'中,我会创建一个新的托管对象上下文并对其执行提取操作,将结果存储在字典中,然后在主线程中使用它们?当在主线程上使用时,位置字典中存储的“位置”对象是否会导致问题,因为它们是从后台检索的?另外,从背景获取'位置“而不是'Car'对象会有什么不同? – 2013-03-27 10:46:05