核心数据多线程和嵌套上下文
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)仍然阻塞。以上是关于核心数据多线程和嵌套上下文的主要内容,如果未能解决你的问题,请参考以下文章