我正在选择一些核心数据条目并对其进行排序。这很直接,但由于按距离排序的计算,我想把它放在另一个线程中。我通过在下面的#ifdef THREADS
部分中添加代码为另一个线程创建一个新的NSManagedObjectController,这是通常的做法。
现在我可以进行提取,然后进行排序和组织,而不会减慢用户界面的速度,表格将在准备好后进行更新。
但是,由于我在本地托管对象上下文中进行提取,因此我的提取对象在我的主线程上无效 - 实际上它们从我的表视图单元格中消失。
所以,我的问题是:1)使用主要的托管对象上下文是否可以,因为我只是在阅读,而根本没有创建本地上下文? 2)在排序和组织后,是否应该使用搜索管理对象上下文对象中的ObjectID值从主线程上的主要托管对象上下文中重新获取所有对象? 3)我应该使用PerformBlockAndWait
对主要托管对象上下文进行提取,但是保留排序并使用searchQ
线程上的返回对象进行组织? 4)我没有想过的其他东西?
- (void) fetchShopLocations {
#ifdef THREADS
dispatch_queue_t searchQ = dispatch_queue_create( "searchQueue", NULL );
dispatch_async( searchQ, ^{
#endif
NSError *error;
#ifdef THREADS
// Make a managed object context that can be not on the main thread
NSManagedObjectContext *localContext = [[NSManagedObjectContext alloc] init];
[localContext setPersistentStoreCoordinator: [self.managedObjectContext persistentStoreCoordinator]];
#else
NSManagedObjectContext *localContext = self.managedObjectContext;
#endif
// Get the array of locations sorted by distance
NSArray *locations = NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName: NSStringFromClass([Shop class])];
// No sort descriptors since I cannot sort on things not in the database
NSArray *locations = [context executeFetchRequest: request error: &error];
if( !locations ) {
NSLog( @"Shop fetch error: %@", error );
return;
}
locations = [locations sortedArrayUsingComparator: ^NSComparisonResult( Shop *loc1, Shop *loc2 ) {
double dist1 = [loc1 distanceFromLatitude: self.referenceLatitude andLongitude: self.referenceLongitude];
double dist2 = [loc2 distanceFromLatitude: self.referenceLatitude andLongitude: self.referenceLongitude];
if( dist1 < dist2 )
return NSOrderedAscending;
else if( dist1 > dist2 )
return NSOrderedDescending;
return [[loc1 name] localizedCaseInsensitiveCompare: [loc2 name]];
}];
NSMutableArray *sections = [NSMutableArray arrayWithCapacity: 5];
NSMutableDictionary *section;
NSMutableArray *rows;
section = [NSMutableDictionary dictionaryWithCapacity: 2];
rows = [NSMutableArray arrayWithCapacity: locations.count];
__block double sectionLimitUserUnits = 5.0;
__block double sectionLimitKilometers;
if( self.userUnits == LocationDistanceUnitsMiles )
sectionLimitKilometers = sectionLimitUserUnits * KILOMETERS_PER_MILE;
else sectionLimitKilometers = sectionLimitUserUnits;
[section setValue: [NSString stringWithFormat: @"Within %g", sectionLimitUserUnits] forKey: kSectionHeaderTitleKey];
[locations enumerateObjectsUsingBlock: ^( Shop *loc, NSUInteger idx, BOOL *stop ) {
double distance = [loc distanceFromLatitude: self.referenceLatitude andLongitude: self.referenceLongitude];
if( distance > self.maxDistance ) {
*stop = YES;
} else {
while( distance > sectionLimitKilometers ) {
[section setValue: [rows copy] forKey: kRowKey];
[sections addObject: [section copy]];
[section removeAllObjects];
[rows removeAllObjects];
sectionLimitUserUnits += 5.0;
if( self.userUnits == LocationDistanceUnitsMiles )
sectionLimitKilometers = sectionLimitUserUnits * KILOMETERS_PER_MILE;
else sectionLimitKilometers = sectionLimitUserUnits;
[section setValue: [NSString stringWithFormat: @"Within %g", sectionLimitUserUnits] forKey: kSectionHeaderTitleKey];
}
[rows addObject: loc];
}
}];
[section setValue: [rows copy] forKey: kRowKey];
[sections addObject: [section copy]];
#ifdef THREADS
dispatch_async( dispatch_get_main_queue(), ^{
#endif
self.sections = sections;
[self.tableView reloadData];
#ifdef THREADS
});
});
dispatch_release( searchQ );
#endif
}
由于线程冲突的非确定性,我知道我不能仅仅依靠测试模拟器中看起来有用的东西。
提前致谢!
答案 0 :(得分:0)
我强烈建议您将NSManagedObjectContext上的所有操作保留在单个线程或串行队列中。使用iOS5,您可以使用专用队列并发类型创建NSManagedObjectContext,并在该上下文中处理performBlock方法中的所有操作。因此,要回答问题1,只能在与其关联的线程或队列中访问NSManagedObjectContext及其关联。如果是为另一个队列创建的,请不要在主队列中访问它。它会给你带来头痛的道路!
问题2是最好的方法。在后台进行艰苦的工作,然后只在主线程上获取所需的内容,这是一种很好的,安全的方式来处理你想要做的事情。
问题3:如果您的主要托管对象是使用NSMainQueueConcurrencyType并发类型创建的,那么您不需要在主线程上执行performBlock:或performBlockAndWait :.如果你需要从后台线程做一些事情,你只需要使用它们。
另外,我强烈推荐MagicalRecord。它让我的生活更轻松地处理核心数据,并且显着减少了我为编写简单的事情而必须编写的样板代码量。
〜詹姆斯
答案 1 :(得分:0)
1)使用主要的托管对象上下文是否可以,因为我只是在阅读,而根本不创建本地上下文?
没有。总有一天,有些东西会同时使用fetch访问主要的托管对象上下文,这将是一个令人头痛的问题。 (我遇到了这样的死锁。)
2)在排序和组织后,是否应该使用搜索管理对象上下文对象中的ObjectID值从主线程上的主要托管对象上下文中重新获取所有对象?
不是所有对象,因为您已经在使用表视图,所以您只需要重新获取将在表中显示的对象。使用ObjectID从主线程重新获取对象是正确的做法。
3)我应该使用
PerformBlockAndWait
对主要托管对象上下文进行提取,但是保留排序并使用searchQ
线程上的返回对象进行组织
这是另一种选择,但你需要理解非常好它是如何工作的。你正在做的方法实际上更简单。
4)我没有想过的其他东西?
您可以通过创建返回ORDER BY距离的查询来避免在代码中对数组进行排序。请参阅this answer。