后台位置的核心数据更新位置导致阻止UI

时间:2015-07-29 14:41:19

标签: ios objective-c core-data ios8 nsmanagedobjectcontext

我正在使用3个托管对象上下文体系结构(为后台创建临时文本,其中父文件是managedObjectContext - UI,并且具有应该在后台写入数据库的父writerObjectContext),我在更新对象时遇到阻止UI的问题。例子是最好的。所以我的数据库中有数千个点,我使用NSFetchedResultsControllertableView来获取它们。这是我的代码:

- (void)viewDidLoad
{
    [super viewDidLoad];

    temporaryContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
    temporaryContext.parentContext = [[CoreDataManager manager] managedObjectContext];
    temporaryContext.undoManager = nil;

    ...
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{   
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:PositionCellIdentifier forIndexPath:indexPath];
    [self configureCell:(PanelPositionCell *)cell atIndexPath:indexPath];
    return cell;
}

- (void)configureCell:(PanelPositionCell *)cell atIndexPath:(NSIndexPath *)indexPath {
    // Fetch Record
    NSManagedObject *record = [self.fetchedResultsController objectAtIndexPath:indexPath];
    OpenPositionCD *position = (OpenPositionCD *)record;

    // Update Cell
    [cell setValuesByOpenPositionCD:position];

    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{
        [self checkAddress:position];
    });
}

- (void)checkAddress:(OpenPositionCD *)openPosition {
    if (openPosition.latitude == 0 && openPosition.longitude == 0) {
        return;
    }

    if ([openPosition hasAddress]) {
        return;
    }

    CLLocation *location = [[CLLocation alloc]initWithLatitude:[openPosition.latitude doubleValue] longitude:[openPosition.longitude doubleValue]];
    [[LocationManager manager] getPlacemarksForLocation:location withCompletion:^(NSArray *placemarks, NSError *error) {
        if (!error) {
                openPosition.address = placemarks[0];
                            NSError *error = nil;
                if (![temporaryContext save:&error]) {
                    NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
                }
        }
    }];
}

当我滚动到没有地址的单元格时,UI经常冻结,这取决于我滚动的速度。那么我该如何解决呢?我正在尝试使用/不使用dispatch_async和使用/不使用temporaryContext performBlock,但看起来没什么可以帮助我。所以感谢任何帮助。

我在CoreDataManager中添加了上下文的初始化,但我希望它没问题:

// Returns the managed object context for the application.
// If the context doesn't already exist, it is created and bound to the persistent store coordinator for the application.
- (NSManagedObjectContext *)managedObjectContext{
    if (_managedObjectContext != nil) {
        return _managedObjectContext;
    }

    _managedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];
    _managedObjectContext.parentContext = [self writerManagedObjectContext];

    return _managedObjectContext;
}

// Writer context for database
- (NSManagedObjectContext *)writerManagedObjectContext{
    if (_writerManagedObjectContext != nil) {
        return _writerManagedObjectContext;
    }

    NSPersistentStoreCoordinator *coordinator = [self persistentStoreCoordinator];
    if (coordinator != nil) {
        _writerManagedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
        [_writerManagedObjectContext setPersistentStoreCoordinator:coordinator];
    }
    return _writerManagedObjectContext;
}

1 个答案:

答案 0 :(得分:2)

You are using outdated APIs. The recommended way to use multiple contexts is not to assign the same persistent store coordinator to the child context, but instead assign it a parentContext.

You probably want M. Zarra's setup with

WriterContext (background)
MainContext (main thread, parent is WriterContext)
WorkerContext (background, parent is MainContext, create and destroy as needed)

You would do the background work in a worker context and save which would push the changes to the main context. You can save the main context when it is convenient, and the data store only gets hit in the background when the writer context is saving.

Finally, you are using the position object in a different thread. You need to wrap your calls into a worker context's performBlock block to safely use these objects.