根据子对象中的可能属性快速筛选父项NSManagedObjects
我正在处理需要一些相当复杂的筛选的项目。我有一个Race
NSManagedObject具有附加数量的Car
对象。 Car
可以有多个Position
对象。对于特定的比赛,我需要获得所有赛车的列表,但是根据比赛是否正在进行,他们需要按不同的顺序排列。如果比赛尚未开始,他们需要订购Car.number
(简单)。如果比赛正在进行,他们需要按照当前位置排序。困难在于,检索的位置不能是该组中的最新位置,而是最近在特定范围内并且可能根本没有位置(即,如果赛车不在比赛中)。根据子对象中的可能属性快速筛选父项NSManagedObjects
的排序是所有做,我有一些代码的工作,让我根据获取的全套位置为汽车,然后用filteredSetUsingPredicate:
和valueForKeyPath:
检索最大时间对我Car
设置currentPosition
属性。与此相关的问题是,它非常缓慢,而且这需要很快(因为它每秒都会发生)。我把它封装在dispatch_async
内,但是由于NSManagedObjects不是线程安全的,导致偶尔死机。
我非常需要的是一种方式来获得那些Car.currentPosition
可选如果适用(这是由获得最后Position.time
最后10秒,如果它存在内确定)设置为最新的Position
Car
对象的一个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 - 汽车的在比赛中的位置即第一个)
道歉可能是过分简单化,但简而言之,这是应该(可以)工作。
- (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很大交易工作;叫我宠坏了。
因此,在'async'中,我会创建一个新的托管对象上下文并对其执行提取操作,将结果存储在字典中,然后在主线程中使用它们?当在主线程上使用时,位置字典中存储的“位置”对象是否会导致问题,因为它们是从后台检索的?另外,从背景获取'位置“而不是'Car'对象会有什么不同? – 2013-03-27 10:46:05
你有自己的两难境地!当你试图将你的提取/计算放在后台线程中时,你是否尝试过并且已经成功地在后台线程中保存排序后的车辆ID,将该组ID通过回调传递给主线程,然后重新获取基于这些ID的主线程中的“NSManagedObjects”?在线程抓取问题中,传递'NSIntegers'已经保存了我的培根, – 2013-03-26 12:17:09
我在'dispatch_async()'中执行'performFetch:'后将所有内容都包装好,然后在运行'completionBlock()'时为主线程执行'dispatch_async' 。我没有尝试传递ID,因为我不确定这是否有助于此实例 - 我需要基于Car.Position.time进行排序,因此我需要后台线程中的整个对象除非我误解? – 2013-03-26 12:23:40
我会捡起一些代码并将其作为答案发布 - 我的代码更好。 – 2013-03-26 12:29:33