0

我正在选择一些核心数据条目并对其进行排序。这很简单,但由于按距离排序的计算,我想把它放在另一个线程中。#ifdef THREADS我通过在下面的部分中添加代码为另一个线程创建一个新的 NSManagedObjectController 来做到这一点。

现在我可以进行提取,然后进行排序和组织,而不会减慢用户界面的速度,并且表格将在准备好后进行更新。

但是,由于我在本地托管对象上下文中进行获取,因此我获取的对象在我的主线程上无效 - 实际上它们从我的表格视图单元格中消失了。

所以,我的问题是:1)使用主托管对象上下文是否可以,因为我只是在阅读,而不是创建本地上下文?2) 在排序和组织之后,我是否应该使用搜索托管对象上下文对象中的 ObjectID 值从主线程上的主托管对象上下文中重新获取所有对象?3)我是否应该在主托管对象上下文中进行提取,然后使用线程PerformBlockAndWait上返回的对象进行排序和组织?searchQ4)我还没有想到的其他东西?

- (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
}

由于线程冲突的不确定性,我知道我不能仅仅依赖于测试在模拟器中似乎可以工作的东西。

提前致谢!

4

2 回答 2

0

我强烈建议您将 NSManagedObjectContext 上的所有操作保留在单个线程或串行队列中。使用 iOS5,您可以使用私有队列并发类型创建 NSManagedObjectContext,并在该上下文的 performBlock 方法中处理所有操作。因此,要回答问题 1,仅在与其关联的线程或队列中访问您的 NSManagedObjectContext 及其关联。如果它是为另一个创建的,请不要在主队列中访问它。它会让你在路上头疼!

问题 2 是最好的方法。在后台完成繁重的工作,然后只在主线程上获取您需要的内容,这是处理您正在尝试做的事情的一种很好、安全的方式。

问题 3:如果您的主托管对象是使用 NSMainQueueConcurrencyType 并发类型创建的,那么您不需要在主线程上执行 performBlock: 或 performBlockAndWait:。如果您需要从后台线程执行某些操作,您只需要使用它们。

另外,我强烈推荐MagicalRecord。它使我使用核心数据的工作变得更加轻松,并显着减少了我为完成简单的事情而必须编写的样板代码量。

~詹姆斯

于 2013-08-24T03:53:15.447 回答
0

1)使用主托管对象上下文可以吗,因为我只是在阅读,根本不创建本地上下文?

不会。总有一天,某些东西会在 fetch 的同时访问主托管对象上下文,这将是一件令人头疼的事情。(我在这样做时遇到了死锁。)

2) 在排序和组织之后,我是否应该使用搜索托管对象上下文对象中的 ObjectID 值从主线程上的主托管对象上下文中重新获取所有对象?

不是所有的对象,因为您已经在使用表格视图,您只需要重新获取将在表格中显示的对象。使用 ObjectID 从主线程重新获取对象是正确的做法。

3)我是否应该在主托管对象上下文中进行提取,然后使用线程PerformBlockAndWait上返回的对象进行排序和组织searchQ

这是另一种选择,但您需要非常了解它的工作原理。你正在做的方法实际上更简单。

4)我还没有想到的其他东西?

您可以通过返回位置 ORDER BY 距离的查询来避免在代码中对数组进行排序。看到这个答案

于 2013-08-25T03:58:12.987 回答