CoreData子上下文,NSFetchedResultsController和主线程

 学习小菜鸟 发布于 2023-01-30 11:06

在Olivier Drobnik的这篇优秀文章之后,我实现了CoreData大师Marcus S. Zarra提出的三层CoreData堆栈:

三层CoreData上下文架构

与此图和我的代码的唯一区别是我只使用一个临时背景MOC,以避免在几个临时MOC中插入对象时出现重复.这是我的上下文初始化代码:

#pragma mark - NSManagedObjectContexts

+ (NSManagedObjectContext *)privateManagedObjectContext
{
    if (!_privateManagedObjectContext) {

        // Setup MOC attached to PSC
        _privateManagedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
        [_privateManagedObjectContext setPersistentStoreCoordinator:[self persistentStoreCoordinator]];

        // Add notification to perform save when the child is updated
        _privateContextSaveObserver =
        [[NSNotificationCenter defaultCenter]
         addObserverForName:NSManagedObjectContextDidSaveNotification
         object:nil
         queue:nil
         usingBlock:^(NSNotification *note) {
             NSManagedObjectContext *savedContext = [note object];
             if (savedContext.parentContext == _privateManagedObjectContext) {
                 [_privateManagedObjectContext performBlock:^{
                     NSLog(@"AMBCoreData -> saving privateMOC");
                     NSError *error;
                     if (![_privateManagedObjectContext save:&error]) {
                         NSLog(@"AMBCoreData -> error saving _privateMOC: %@ %@", [error localizedDescription], [error userInfo]);
                     }
                 }];
             }
         }];
    }
    return _privateManagedObjectContext;
}

+ (NSManagedObjectContext *)mainUIManagedObjectContext
{
    if (!_mainUIManagedObjectContext) {

        // Setup MOC attached to parent privateMOC in main queue
        _mainUIManagedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];
        [_mainUIManagedObjectContext setParentContext:[self privateManagedObjectContext]];

        // Add notification to perform save when the child is updated
        _mainUIContextSaveObserver =
        [[NSNotificationCenter defaultCenter]
         addObserverForName:NSManagedObjectContextDidSaveNotification
         object:nil
         queue:nil
         usingBlock:^(NSNotification *note) {
             NSManagedObjectContext *savedContext = [note object];
             if (savedContext.parentContext == _mainUIManagedObjectContext) {
                 NSLog(@"AMBCoreData -> saving mainUIMOC");
                 [_mainUIManagedObjectContext performBlock:^{
                     NSError *error;
                     if (![_mainUIManagedObjectContext save:&error]) {
                         NSLog(@"AMBCoreData -> error saving mainUIMOC: %@ %@", [error localizedDescription], [error userInfo]);
                     }
                 }];
             }
         }];
    }
    return _mainUIManagedObjectContext;
}

+ (NSManagedObjectContext *)importManagedObjectContext
{
    if (!_importManagedObjectContext) {

        // Setup MOC attached to parent mainUIMOC in private queue
        _importManagedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
        [_importManagedObjectContext setParentContext:[self mainUIManagedObjectContext]];
    }
    return _importManagedObjectContext;
}

这段代码非常简单.我只使用mainUIManagedObjectContext中的复制上面的图表NSMainQueueConcurrencyType.每次importManagedObjectContext保存子上下文时,都会触发通知,并且所有父上下文都会在其当前线程中执行保存.

我已经实现了一个带有a UITableViewNSFetchedResultsController附加的测试视图控制器.这是viewDidLoad我的测试视图控制器中的代码:

- (void)viewDidLoad
{
    [super viewDidLoad];

    NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:@"Task"];
    [request setSortDescriptors:@[[NSSortDescriptor sortDescriptorWithKey:@"insertDate" ascending:NO]]];
    self.fetchRequest = request;
    NSFetchedResultsController *frc =
    [[NSFetchedResultsController alloc]
     initWithFetchRequest:self.fetchRequest
     managedObjectContext:[AMBCoreData mainUIManagedObjectContext]
     sectionNameKeyPath:nil
     cacheName:nil];
    frc.delegate = self;

    [self setFetchedResultsController:frc];
    [self.fetchedResultsController performFetch:nil];
}

在这里,我附上mainUIManagedObjectContextNSFetchedResultsController.后来,在我的viewDidAppear运行循环中插入了几个Task实体:

- (void)viewDidAppear:(BOOL)animated
{
    [super viewDidAppear:animated];
    [[AMBCoreData importManagedObjectContext] performBlock:^{
        for (int i = 0; i < 5000; i++) {
            Task *task = [NSEntityDescription insertNewObjectForEntityForName:@"Task" inManagedObjectContext:[AMBCoreData importManagedObjectContext]];
            task.title = [NSString stringWithFormat:@"Task %d", i];
            task.insertDate = [NSDate new];
        [[AMBCoreData importManagedObjectContext] save:nil];
    }];
}

问题是,我正在插入5000个对象,当数据填充到表视图中时,UI正在冻结.Florian Kugler 用这个架构进行了测试,插入了15.000个对象和乐器,他得到了这个主线程用法(蓝色用于主线程,灰色用于任何其他线程):

具有三层上下文体系结构的主线程CPU使用率

但这是我的主要线程CPU使用5000个对象,使用iPhone 5进行分析:

在此输入图像描述

正如您所看到的,我的主要线程使用量远远大于Florian的,并且我的UI冻结了几秒钟.我的问题是,我做错了什么?这是使用这个三层MOC架构a NSFetchedResultsController和a 时的预期行为UITableView吗?我知道插入5000个对象并不是大多数应用程序的常用行为,所以当我尝试使用50或100个对象时,冻结是不存在或不明显的,但主线程使用率很高(尽管我承认在这种情况下它可以是因为其他原因,如唤醒应用程序).

1 个回答
  • 是的,这是预期的,因为主要的MOC参与其子女的拯救.当UI上下文的子项不进行大的保存时,它很方便,也很好,但如果这些保存更大,则常常会成为性能问题.使用此模式时,无法确定UI线程仅执行最小作业.

    对于大型保存,我建议创建一个直接使用持久性存储协调器配置的上下文.发生大保存后,您只需在UI上下文中重新获取并可选地刷新数据.欲了解更多详情,请参阅我的答案在这里.

    2023-01-30 11:09 回答
撰写答案
今天,你开发时遇到什么问题呢?
立即提问
热门标签
PHP1.CN | 中国最专业的PHP中文社区 | PNG素材下载 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有