1.Core Data线程安全问题:
在一些简单项目中,一般只需要一个ManagedContext在主线程就可以了,但对于复杂项目来说,如果都放在主线程, 面对大数据量的读写必然阻塞主线程,用户体验极差。ManagedContext不是线程安全的,如果想要多线程访问CoreData的话,最好的方法是一个线程一个NSManagedObjectContext,每个NSManagedObjectContext对象实例都可以使用同一个NSPersistentStoreCoordinator实例,这个实例可以很安全的顺序访问永久存储,这是因为NSManagedObjectContext会在便用NSPersistentStoreCoordinator前上锁。
所以使用CoreData的并行主要有两种方式Notificaiton child/parent context。
(1)Notificaiton
不同的线程使用不同的context进行操作,当一个线程的context发生变化后,利用notification来通知另一个线程Context,另一个线程调用mergeChangesFromContextDidSaveNotification来合并变化。
Notification的种类
NSManagedObjectContextObjectsDidChangeNotification 当Context中的变量改变时触发。
NSManagedObjectContextDidSaveNotification 在一个context调用save完成以后触发。注意,这些managed object只能在当前线程使用,如果在另一个线程响应通知,要调用mergeChangesFromContextDidSaveNotification来合并变化。
NSManagedObjectContextWillSaveNotification。将要save。
上个例子:
NSManagedObjectContext *context = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
[context performBlock:^{
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(mocDidSaveNotification:) name:NSManagedObjectContextObjectsDidChangeNotification object:nil];
YYBHouseDetail *house = (YYBHouseDetail *)[NSEntityDescription insertNewObjectForEntityForName:@"YiYibnb" inManagedObjectContext:context];
house.title = @"123";
NSError *error ;
if (![context save:&error]) {
NSLog(@"error is %@",error);
}
}];
-(void)mocDidSaveNotification:(NSNotification *)notification {
NSManagedObjectContext *context = [notification object];
if (_myAppDelegate.managedObjectContext==saveContext) {
return;
}
if (_myAppDelegate.managedObjectContext.persistentStoreCoordinator!=saveContext.persistentStoreCoordinator) {
return;
}
dispatch_sync(dispatch_get_main_queue(), ^{
[_myAppDelegate.managedObjectContext mergeChangesFromContextDidSaveNotification:notification];
});
}
ChildContext和ParentContext是相互独立的。只有当ChildContext中调用Save了以后,才会把这段时间来Context的变化提交到ParentContext中,ChildContext并不会直接提交到NSPersistentStoreCoordinator中, parentContext就相当于它的NSPersistentStoreCoordinator。
通常主线程context使用NSMainQueueConcurrencyType,其他线程childContext使用NSPrivateQueueConcurrencyType. child和parent的特点是要用Block进行操作,performBlock,或者performBlockAndWait,保证线程安全。这两个函数的区别是performBlock不会阻塞运行的线程,相当于异步操作,performBlockAndWait会阻塞运行线程,相当于同步操作。
举例:
self.privateContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType]; [self.privateContext setParentContext:self.mainContext]; //执行耗时的操作 //执行完毕 [self.privateContext performBlock:^{ NSError * error = nil; if ([self.privateContext hasChanges]) { [self.privateContext save:&error]; } }];2.Core Data 系统BUG
当数据库中存在一对多且有序时,我们利用Model生成NSManagedObject文件中一对多有序关系会生成一个OrderSet,调用系统自动生成的插入删除方法时,程序有时候会崩,没错,这就是Core Data 的一个BUG,现在还苹果官方没有给出解决方案。我们在依依短租项目中的解决方案是重写系统生成的方法。在依依项目中,一个房子对应多张图片并且是有序的,根据Model生成的房屋类中存在@property (nullable, nonatomic, retain) NSOrderedSet<Image *> *images;这个属性,及下列方法:
- (void)insertObject:(Image *)value inImagesAtIndex:(NSUInteger)idx;
- (void)removeObjectFromImagesAtIndex:(NSUInteger)idx;
- (void)insertImages:(NSArray<Image *> *)value atIndexes:(NSIndexSet *)indexes;
- (void)removeImagesAtIndexes:(NSIndexSet *)indexes;
- (void)replaceObjectInImagesAtIndex:(NSUInteger)idx withObject:(Image *)value;
- (void)replaceImagesAtIndexes:(NSIndexSet *)indexes withImages:(NSArray<Image *> *)values;
- (void)addImagesObject:(Image *)value;
- (void)removeImagesObject:(Image *)value;
- (void)addImages:(NSOrderedSet<Image *> *)values;
- (void)removeImages:(NSOrderedSet<Image *> *)values;
我以重写insertObject为例,其余重写方法也类似,就不在贴代码了:
static NSString *image = @"images";
- (void)insertObject:(Image *)value inImagesAtIndex:(NSUInteger)idx {
NSIndexSet* indexes = [NSIndexSet indexSetWithIndex:idx];
[self willChange:NSKeyValueChangeInsertion valuesAtIndexes:indexes forKey:image];
NSMutableOrderedSet *tmpOrderedSet = [NSMutableOrderedSet orderedSetWithOrderedSet:[self mutableOrderedSetValueForKey:image]];
[tmpOrderedSet insertObject:value atIndex:idx];
[self setPrimitiveValue:tmpOrderedSet forKey:image];
[self didChange:NSKeyValueChangeInsertion valuesAtIndexes:indexes forKey:image];
}