核心数据多线程和嵌套上下文

Posted

技术标签:

【中文标题】核心数据多线程和嵌套上下文【英文标题】:Core data multithreading and nested contexts 【发布时间】:2013-11-26 11:06:42 【问题描述】:

我刚刚开始学习核心数据编程。我试图举一个例子,其中有一个显示人员列表的表格视图(属性:名字,姓氏)。表格视图依赖于 NSFetchResultController 来显示人员列表。

我遵循嵌套上下文模式如下:

根上下文 (NSPrivateQueueConcurrencyType) 主上下文 (NSMainQueueConcurrencyType) 子上下文 (NSPrivateQueueConcurrencyType)。

子上下文用于执行大量插入/获取(使用 perormBlock: 方法)。 当我尝试执行大量插入(大约 5000 行)时,先保存子上下文,然后保存主上下文,然后保存根上下文,我看到我的 UI 被阻塞,直到保存完成。

谁能告诉我为了制作高性能应用程序而采用的最佳解决方案是什么?谁能给我一个很好的简单代码,展示如何在不阻塞 UI 的情况下在后台进行大量提取/插入?

    [_indicator startAnimating];

NSManagedObjectContext *aContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
aContext.parentContext = [[SDCoreDataController sharedInstance] mainManagedObjectContext];

[aContext performBlock:^

    NSError *error;

    for (int i = 0; i < 5000; i++)
    
        FootBallCoach *backgroundCoach = [NSEntityDescription insertNewObjectForEntityForName:@"FootBallCoach" inManagedObjectContext:aContext];

        backgroundCoach.firstName = [NSString stringWithFormat:@"José %i",i];
        backgroundCoach.lastName = [NSString stringWithFormat:@"Morinho %i",i];
        backgroundCoach.cin = [NSString stringWithFormat:@"%i",i];

        if (i % 50 == 0)
        
            [aContext save:&error];

            [aContext reset];
        
    

    [[SDCoreDataController sharedInstance] saveMainContext];
    [[SDCoreDataController sharedInstance] saveRootContext];

    dispatch_async(dispatch_get_main_queue(), ^

        [_indicator stopAnimating];

        [self refreshCoaches:nil];
    );

];

【问题讨论】:

developer.apple.com/library/content/documentation/Cocoa/… 【参考方案1】:

不要进行“大量”进口。 每次对存储执行写入操作时,NSPersistentStoreCoordinator 都会锁定存储以进行任何其他类型的操作。因此,如果您的 UI 在此期间尝试获取数据,它将被阻止。

将您的保存分割为 100~200 个对象(取决于对象的大小和复杂性)。 分割实际上取决于您的对象图结构,伪代码将是:

编辑:我已编辑代码以反映对您的保存过程的更正。 您保存到商店的文件(实际文件)也应该被分段,否则您仍然会得到一个“巨大的”保存操作。

for ( i = 0; i < LARGE_N; i += BATCHSIZE)

    @autoreleasepool 
        batchInfo = importInfos[i : MIN(i+BATCHSIZE-1,LARGE_N-1]; //array of the batch
        //use existing objects or create new ones if needed
        //use batch fetching to reduce existing items find time
        batchInfo = createOrReuseItemsForBatchInfo(batchInfo);
        //you can also practice weeding:
        //  create all items as newly inserted
        //  after batch insertion completed, find existing items, 
        //     replace them with the newly inserted and delete the duplicated inserted objects.
        //save all the way to the store
        NSManagedObjectContext* ctx = context;
        __block BOOL saveSuccessful = YES;
        while(ctx && saveSuccessful) 
            [ctx performBlockAndWait:^
                saveSuccessful = [ctx save:&error]
            ];
            ctx = ctx.parentContext;
        
        //handle unsuccessful save
        [context  reset];
        //You can discard processed objects from importInfos array if you like
    

【讨论】:

现在我正在运行一个 for 循环并在每次插入 100 个项目时保存。您是否确认嵌套上下文方法是最好的方法? 当我的数据库第一次为空时,它工作正常。但是当我有大约 20 000 个项目并尝试插入 5000 个新项目时,UI 被阻止。 如果您保存了 100 个项目,并且 UI 仍然被阻止,请尝试减小批量大小。嵌套上下文架构适用于某些事情(根据我的经验,主要是小型导入和简单的存储操作)。如果您需要最大的并发性,您可以使用多协调器环境(但是您需要将更改合并到您的主要上下文中,这本身就是一个故事)。 5K 项插入总是很慢,因为写入操作会在操作系统级别阻塞文件。正如我所说,使用分段导入(最好在 BG 线程上花费更多时间并执行读取操作,然后过于频繁地长时间阻塞 UI)。 我刚刚尝试了多协调器环境方法,我得到了相同的结果。每个新的插入操作(5000 个项目)我都会使用 performBlock: 方法创建一个单独的上下文,每次插入 100 个项目时我都会保存。我的 tableView(基于 NSFetchResultController)仍然阻塞。

以上是关于核心数据多线程和嵌套上下文的主要内容,如果未能解决你的问题,请参考以下文章

多线程核心数据:“主”上下文线程

带有子上下文的核心数据多线程

iPhone 核心数据和多线程

核心数据和多线程在保存时崩溃

核心数据多线程获取记录

Python核心编程——多线程threading和队列