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

Core Data update locations for positions in background cause blocking UI

我正在使用 3 Managed Object Contexts Architecture(为父级为 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 经常冻结,这取决于我滚动的速度。那么我该如何解决呢?我正在尝试 with/without dispatch_async 和 with/without 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;
}

您正在使用过时的 API。使用多个上下文的推荐方法是 而不是 将相同的持久存储协调器分配给子上下文,而是将其分配给 parentContext

您可能想要 M. Zarra 的设置

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

您将在 worker 上下文中执行后台工作,save 将更改推送到主上下文。您可以在方便的时候保存主上下文,数据存储仅在编写器上下文保存时在后台被访问。

最后,您在另一个线程中使用了位置对象。您需要将调用包装到工作上下文的 performBlock 块中以安全地使用这些对象。