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

Posted

技术标签:

【中文标题】后台位置的核心数据更新位置导致阻塞 UI【英文标题】:Core Data update locations for positions in background cause blocking UI 【发布时间】:2015-07-29 14:41:19 【问题描述】:

我正在使用 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】:

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

你可能想要 M. Zarra 的设置

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

您将在工作人员上下文中执行后台工作,save 会将更改推送到主上下文。您可以在方便时保存主上下文,而数据存储只有在编写器上下文保存时才会在后台命中。

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

【讨论】:

以上是关于后台位置的核心数据更新位置导致阻塞 UI的主要内容,如果未能解决你的问题,请参考以下文章

在 SwiftUI 视图中发布后台上下文 Core Data 更改而不阻塞 UI

在后台保存条目会导致崩溃并仍然阻塞 UI

CoreData从后台线程读取数据仍然阻塞UI界面的原因及解决

写入 coredata,即使在后台线程上完成,也会严重阻塞 UI

核心数据:大后台抓取能否阻塞主线程抓取请求?

semaphore_wait_trap 阻塞 UI