数据库多线程问题

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方法,这样才能实现本地数据持久化。

}]; }]; }  

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,277评论 6 503
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,689评论 3 393
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 163,624评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,356评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,402评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,292评论 1 301
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,135评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,992评论 0 275
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,429评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,636评论 3 334
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,785评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,492评论 5 345
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,092评论 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,723评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,858评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,891评论 2 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,713评论 2 354

推荐阅读更多精彩内容