CoreData子上下文,NSFetchedResultsController和主线程 [英] CoreData child contexts, NSFetchedResultsController and main thread

查看:116
本文介绍了CoreData子上下文,NSFetchedResultsController和主线程的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

以下是奥利维尔·德罗布尼克(Olivier Drobnik)的这篇出色的帖子,我ve实现了CoreData专家Marcus S. Zarra提出的三层CoreData堆栈:

Following this excellent post by Olivier Drobnik, I've implemented the three-layer CoreData stack proposed by CoreData guru Marcus S. Zarra:

与该图和我的代码的唯一区别是,我仅使用一个临时背景MOC,以便在将对象插入多个临时MOC时避免重复.这是我的上下文初始化代码:

The only difference from this diagram and my code is that I only use one Temporary Background MOC, in order to avoid duplicates when inserting objects in several temp MOCs. Here's my context initialisation code:

#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;
}

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

This code is pretty straightforward. I'm replicating the above diagram using only the mainUIManagedObjectContext in the NSMainQueueConcurrencyType. Every time the child context, importManagedObjectContext gets saved, a notification is fired and all the parent contexts performs a save in it's current thread.

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

I've implemented a test view controller with a UITableView and a NSFetchedResultsController attached. This is the code in the viewDidLoad of my test view controller:

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

在这里,我将mainUIManagedObjectContext附加到NSFetchedResultsController.稍后,在我的viewDidAppear中,我运行一个循环以插入一些Task实体:

Here I attach the mainUIManagedObjectContext to the NSFetchedResultsController. Later, in my viewDidAppear, I run a loop to insert a few Task entities:

- (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个对象并使用工具,他得到了这个主线程用法(蓝色代表主线程,灰色代表其他任何线程):

The thing is, I'm inserting 5000 objects and the UI is freezing when the data is populated into the table view. Florian Kugler ran a test with this architecture inserting 15.000 objects and with instruments he got this main thread usage (blue is for main thread, gray for any other threads):

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

But here's my main thread CPU usage with 5000 objects, profiled using an iPhone 5:

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

As you can see, my main thread usage is far greater than Florian's and also my UI freezes for a few seconds. My question is, am I doing something wrong? Is this the expected behaviour when using this three-layer MOC architecture with a NSFetchedResultsController and a UITableView? I know that inserting 5000 objects is not the usual behaviour of most apps, so when I've tried with 50 or 100 objects the freeze was inexistent or unnoticeable, but the main thread usage was high (although I admit that in this case it can be due another reasons like waking up the app).

推荐答案

是的,可以预料,因为Main MOC参与了其子项的保存.当UI上下文的子级不进行大量保存时,这很方便,也可以,但是如果这些保存量较大,通常会导致性能问题.使用这种模式时,您不能确定UI线程仅执行最少的工作.

Yes, it is expected, because Main MOC is involved in the saves of its children. It is convenient and kind of okay when children of the UI context don’t do big saves, but often becomes a performance problem if those saves are bigger. You can’t be sure that the UI thread does only minimum job when using this pattern.

为节省大量资金,我建议创建一个直接由持久性存储协调器配置的上下文.发生大量保存后,您只需重新获取UI上下文中的数据并有选择地刷新数据即可.有关更多详细信息,请在此处.

For the big saves I would recommend creating a context that is configured directly with the persistent store coordinator. After big save happens, you just refetch and optionally refresh data in the UI context. For more details see my answer here.

这篇关于CoreData子上下文,NSFetchedResultsController和主线程的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆