对于某个比较耗时的数据库操作,我们可以采取多线程的方式,避免阻塞UI线程。本文的完整代码,可以点击这里下载
Core Data的多线程操作,主要涉及两个问题。
1.如何使用多线程?
2.子线程中的更新如何通知主线程?
网上类似的教程非常多,这里就不细说了。主要思路就是,NSManagedObjectContext并不是线程安全的,而NSPersistentStoreCoordinator则是线程安全的。
所以,当我们需要在子线程中,进行CRUD的时候,我们需要新建一个NSManagedObjectContext,同时可以重用此前的NSPersistentStoreCoordinator。因此,NSPersistentStoreCoordinator更像是一个单例类。
在我使用的Xcode 6.3版本里面,勾选上Core Data创建的应用,Core Data的基本代码已经在AppDelegate里面实现了,由于AppDelegate本身就是一个单例类,所以,需要使用NSPersistentStoreCoordinator类对象的时候,直接用AppDelegate的就好了。
关于这两者之间的关系,直接盗用苹果官网的图片,感觉解释得已经非常清楚了。
以下一段代码,演示了如何使用多线程进行CRUD操作。
- (void)viewDidLoad {
[super viewDidLoad];
AppDelegate *delegate = (AppDelegate *)[UIApplication sharedApplication].delegate;
[delegate.managedObjectContext insertTeamWithName:@"Heat"city:@"Miami"];
[delegate.managedObjectContext insertTeamWithName:@"Lakers"city:@"LA"];
[delegate.managedObjectContext insertTeamWithName:@"Thunder"city:@"Oklahoma"];
[delegate saveContext];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSManagedObjectContext *tmpContext = [[NSManagedObjectContext alloc] init];
[tmpContext setPersistentStoreCoordinator:delegate.persistentStoreCoordinator];
[tmpContext insertTeamWithName:@"76ers" city:@"Philadelphia"];
[tmpContext saveContext];
NSLog(@"%@",[NSThread currentThread]);
dispatch_async(dispatch_get_main_queue(), ^{
NSLog(@"%@",[NSThread currentThread]);
self.teamArray = [delegate.managedObjectContext fetchTeamList];
if (self.teamArray) {
for (Team *teamObject in self.teamArray) {
NSLog(@"Team info : %@, %@\n", teamObject.name, teamObject.city);
}
}
});
});
}
需要注意的是,这里的insertTeamWithName方法,在NSManagedObjectContext的extension中被实现。之所以不放在AppDelegate中实现,是因为创建的临时NSManagedObjectContext对象因此也可以调用这些方法,同时面向用户的CRUD操作,仅需要知道NSManagedObjectContext对象即刻,对于底层的实现并不关心。
为了实现主线程和子线程中的数据同步,我们还需要使用一个通知。使得在saveContext的时候,通知主线程中的context更新。
以下这段代码在Appdelegate.m中被实现。
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(mocDidSaveNotification:) name:NSManagedObjectContextDidSaveNotification object:nil];
- (void)mocDidSaveNotification:(NSNotification *)notification
{
NSManagedObjectContext *savedContext = [notification object];
if (savedContext == self.managedObjectContext) {
return ;
}
if (savedContext.persistentStoreCoordinator != self.persistentStoreCoordinator) {
return ;
}
dispatch_async(dispatch_get_main_queue(), ^{
NSLog(@"Merge changes from other context.\n");
[self.managedObjectContext mergeChangesFromContextDidSaveNotification:notification];
});
}
在mocDidSaveNotification方法中,有两个if判断。第一个判断保证自己不会合并自己的更新,第二个判断的作用在于,如果一个App使用了不止一套context时(比如一套context专门负责学生、成绩等业务相关信息。另一个套context负责app本身的信息),一个context不会合并另一套context的信息。
最后的打印结果如下:
2015-06-22 22:31:27.680 MultiThreadCoreData[4369:3916642] <NSThread: 0x7fb13b6117b0>{number = 2, name = (null)}
2015-06-22 22:31:27.722 MultiThreadCoreData[4369:3916522] Merge changes from other context.
2015-06-22 22:31:27.723 MultiThreadCoreData[4369:3916522] <NSThread: 0x7fb13b608220>{number = 1, name = main}
2015-06-22 22:31:27.724 MultiThreadCoreData[4369:3916522] Team info : 76ers, Philadelphia
2015-06-22 22:31:27.724 MultiThreadCoreData[4369:3916522] Team info : Heat, Miami
2015-06-22 22:31:27.724 MultiThreadCoreData[4369:3916522] Team info : Lakers, LA
2015-06-22 22:31:27.724 MultiThreadCoreData[4369:3916522] Team info : Thunder, Oklahoma
可以看到,子线程中被添加的数据,成功的保存,而且主线程的context及时的更新了数据。