我正在开发一个需要一些相当复杂的过滤的项目。我有一个Race
NSManagedObject,它附加了许多Car
个对象。 Car
可以包含多个Position
个对象。对于特定的比赛,我需要获得所有赛车的列表,但是根据比赛是否正在运行,他们需要根据不同的顺序进行排序。如果比赛尚未开始,则需要Car.number
(简单)进行排序。如果比赛正在进行,则需要按当前位置进行排序。难点在于检索到的位置不能是集合中的最新位置,而是特定范围内的最新位置,并且可能根本没有位置(即,如果汽车不在比赛中)。
排序全部完成,我有一些代码可以让我根据检索汽车的全套位置然后使用{{1}在我的currentPosition
上设置Car
属性}和filteredSetUsingPredicate:
来检索最长时间。这个问题是它很慢,而且需要非常快(每秒都会发生)。我把它包装在valueForKeyPath:
中,但由于NSManagedObjects不是线程安全的,因此导致了偶然的冻结。
我理想情况下需要一种方法来获取dispatch_async
个Car
个对象的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,其中包含自比赛开始以来的秒数 - 这用于确定应显示哪些位置(因为位置可以提前很长时间保存)。
型号:
汽车
位置
答案 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();
});
});
}
我在处理其他线程中的上下文时通常使用MagicalRecord,it simplifies it 伟大的交易;叫我被宠坏了。