1.FMDB线程安全的实现
应用中不可在多个线程中共同使用一个FMDatabase对象操作数据库,这样会引起数据库数据混乱。如果要实现多线程,就需要使用FMDatabaseQueue。
FMDatabaseQueue *queue = [[FMDatabaseQueue alloc] initWithPath:[self getDatabasePath]];
用FMDatabaseQueue来创建队列,所有的操作都在block中执行。
//插入数据
[queue inDatabase:^(FMDatabase *database) {
//写入数据
sql = @"insert into Test (name,image) values (?,?)";
[database executeUpdate:sql,@"张三",data];
}];
实现原理:在应用中只有一个FMDatabaseQueue实例,所有的任务都使用这一个共同的实例。FMDatabaseQueue其实是创建了一个gcd串行队列,放入队列中的block都串行执行,这就保证了同一时间队列中只执行一个操作,避免数据的混乱。
2.事务的概念
如果要保证多个操作同时成功或同时失败,可以把多个操作放到事务里。
[queue inTransaction:^(FMDatabase *db,BOOL*rollback) {
[db executeUpdate:@"INSERT INTO myTable VALUES (?)", [NSNumbernumberWithInt:1]];
[db executeUpdate:@"INSERT INTO myTable VALUES (?)", [NSNumbernumberWithInt:2]];
[db executeUpdate:@"INSERT INTO myTable VALUES (?)", [NSNumbernumberWithInt:3]];
if(whoopsSomethingWrongHappened) { *rollback =YES;return;如果发生错误就进行回滚,把之前已经执行的任务都取消}// ...
[db executeUpdate:@"INSERT INTO myTable VALUES (?)", [NSNumbernumberWithInt:4]];}];
3.coredata多线程
1.初始化moc(托管对象上下文,NSManagedObjectContext)的时候可以指定并发队列。
NSPrivateQueueConcurrencyType 私有并发队列类型,操作在子线程完成。
NSMainQueueConcurrencyType 主并发队列类型,涉及到UI相关操作应该使用这个队列。
2.调用方式,不能直接在不同的线程里使用同一个moc,会造成数据混乱:
- (void)performBlock:(void(^)())block 异步执行的block,调用之后会立刻返回。
- (void)performBlockAndWait:(void(^)())block 同步执行的block,调用之后会等待这个任务完成,才会继续向下执行。
比如说,在多线程的环境下执行MOC的save方法,就是将save方法放在MOC的block体中异步执行,其他方法的调用也是一样的。
[context performBlock:^{
[context save:nil];异步执行save
}];⚠️:这种方法和我们之前做线程切换的方式是不一样的,不能自己开一个子线程然后到子线程里面去执行moc,应该是调用moc自身绑定的队列,把操作放到block里去执行。
iOS5之前使用多个moc:可能有多个MOC关联在同一个PSC上,当一个MOC发生改变并持久化到本地时,系统并不会将其他MOC缓存在内存中的NSManagedObject对象改变。所以这就需要我们在MOC发生改变时,将其他MOC数据更新。
多个moc关联同一个psc,在通知中心注册NSNotification。
// 创建主队列MOC,用于执行UI操作NSManagedObjectContext*mainMOC = [[NSManagedObjectContextalloc] initWithConcurrencyType:NSMainQueueConcurrencyType]; mainMOC.persistentStoreCoordinator = PSC;
// 创建私有队列MOC,用于执行其他耗时操作NSManagedObjectContext*backgroundMOC = [[NSManagedObjectContextalloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType]; backgroundMOC.persistentStoreCoordinator = PSC;
// 通过监听NSManagedObjectContextDidSaveNotification通知,来获取所有MOC的改变消息 [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(contextChanged:) name:NSManagedObjectContextDidSaveNotification object:nil];
// MOC改变后的通知回调
- (void)contextChanged:(NSNotification *)noti {
NSManagedObjectContext *MOC = noti.object;
[MOC performBlock:^{ // 直接调用系统提供的同步API,系统内部会完成同步的实现细节。 [MOC mergeChangesFromContextDidSaveNotification:noti]; }];}
iOS5之前的数据同步:
简单数据冲突,一个moc数据发生改变,提交存储区,其他moc并没有对这个改变的数据进行更新。
有三种通知方式:
NSManagedObjectContextWillSaveNotification
NSManagedObjectContextDidSaveNotification
NSManagedObjectContextObjectsDidChangeNotification
监听到通知以后调用同步api,mergeChangesFromContextDidSaveNotification:noti
复杂数据同步:一个moc对本地数据的存储做出了改变,另一个moc也对相同的数据存储做了改变,这样在save的时候就产生了数据冲突。
这种情况下可以设置moc的mergePolicy属性来指定解决冲突的具体方案。一共有五种属性值。
NSErrorMergePolicy: 默认值,当出现合并冲突时,返回一个NSError对象来描述错误,而MOC和持久化存储区不发生改变。
NSMergeByPropertyStoreTrumpMergePolicy: 以本地存储为准,使用本地存储来覆盖冲突部分。
NSMergeByPropertyObjectTrumpMergePolicy: 以MOC的为准,使用MOC来覆盖本地存储的冲突部分。
NSOverwriteMergePolicy: 以MOC为准,用MOC的所有NSManagedObject对象覆盖本地存储的对应对象。
NSRollbackMergePolicy: 以本地存储为准,MOC所有的NSManagedObject对象被本地存储的对应对象所覆盖。
iOS5之后使用多个moc:
关联了PSC的MOC是parentMOC,parentMOC执行save操作时真正将数据写入了本地数据库。每一个parentMOC都可以有多个childMOC,但是childMOC并不会关联parentMOC,childMOC执行save操作的时候并不会真正把数据写入数据库,而是会通知他的parentMOC自己发生了改变,由parentMOC完成写入数据库的任务。
// 创建PSC实例对象,还是用上面Demo的实例化代码
NSPersistentStoreCoordinator *PSC = self.persistentStoreCoordinator;
// 创建主队列MOC,用于执行UI操作 NSManagedObjectContext *mainMOC = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType]; mainMOC.persistentStoreCoordinator = PSC;
// 创建私有队列MOC,用于执行其他耗时操作,backgroundMOC并不需要设置PSC NSManagedObjectContext *backgroundMOC = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType]; backgroundMOC.parentContext = mainMOC;
[backgroundMOC performBlock:^{
[backgroundMOC save:nil];
[mainMOC performBlock:^{
[mainMOC save:nil];//私有队列调用了save方法之后,主队列也要调用save方法,这样才能实现本地数据持久化。
}]; }]; }