fmdb使用注意问题:
1、导入:导入库 libsqlite3;
2、多线程操作数据库---FMDatabaseQueue
FMResultSet *rs = [db executeQuery:sql,resourceName]; if ([rs next]) { isHtmlExist = YES; }else { isHtmlExist = NO; } [rs close];
FMResultSet 注意结果集的关闭;(单线程操作不关闭,没问题,但是)多线程操作,必须注意,良好的编程习惯很重要;关于 db关闭,写代码的时候没有写,也没报错
3、测试结果
如果涉及到多线程操作数据库 要点:一个线程FMDatabase操作数据库,同时 多个线程使用FMDatabaseQueue操作数据库是不允许的,必须全部使用FMDatabaseQueue操作,(如果同时写入一张标的话)否则,有些情况,会造成死锁。
简而言之:项目中,要么只有一个线程操作数据库,只用FMDatabase;要么多线程操作,全部使用FMDatabaseQueue操作(FMDatabaseQueue,这个类的原理,也是把让多个线程的数据库同步排队操作)
测试过程:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]]; // Override point for customization after application launch. self.window.backgroundColor = [UIColor whiteColor]; [self.window makeKeyAndVisible]; [NSThread detachNewThreadSelector:@selector(writeDbOne) toTarget:self withObject:nil]; [NSThread detachNewThreadSelector:@selector(writeDbTwo) toTarget:self withObject:nil]; [NSThread detachNewThreadSelector:@selector(readDb) toTarget:self withObject:nil]; return YES; } - (void)writeDbOne { DbDao *dao = [DbDao sharedInstance]; for (int i = 0; i < 500; i++) { @autoreleasepool { UserEntity *user = [[[UserEntity alloc] init] autorelease]; user.name = [NSString stringWithFormat:@"name %d", i]; user.password = [NSString stringWithFormat:@"password %d", i]; [dao addUserInMuliThread:user]; NSLog(@"writeDbOne %d ", i); } } } - (void)writeDbTwo { DbDao *dao = [DbDao sharedInstance]; for (int i = 600; i < 1200; i++) { @autoreleasepool { UserEntity *user = [[[UserEntity alloc] init] autorelease]; user.name = [NSString stringWithFormat:@"name %d", i]; user.password = [NSString stringWithFormat:@"password %d", i]; [dao addUser:user]; NSLog(@"writeDbTwo %d ", i); } } }一个线程操作fmdbdatabase,一个线程操作FMDBdatabaseQueue,测试结果:
2014-08-22 17:28:43.615 FMDBThreadTest[2153:3f03] DB Error: 5 "database is locked"
2014-08-22 17:28:43.617 FMDBThreadTest[2153:3f03] DB Query: select * from tbl_user
2014-08-22 17:28:43.618 FMDBThreadTest[2153:3f03] DB Path: /Users/pekall_song/Library/Application Support/iPhone Simulator/7.1/Applications/80F58FBE-96B9-4121-94A0-04018877953E/Library/Caches/denghuihua.sqlite
证明queue采用同步操作的代码:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]]; // Override point for customization after application launch. self.window.backgroundColor = [UIColor whiteColor]; [self.window makeKeyAndVisible]; [NSThread detachNewThreadSelector:@selector(writeDbOne) toTarget:self withObject:nil]; [NSThread detachNewThreadSelector:@selector(writeDbTwo) toTarget:self withObject:nil]; // [NSThread detachNewThreadSelector:@selector(readDb) toTarget:self withObject:nil]; return YES; } - (void)writeDbOne { DbDao *dao = [DbDao sharedInstance]; for (int i = 0; i < 500; i++) { @autoreleasepool { UserEntity *user = [[[UserEntity alloc] init] autorelease]; user.name = [NSString stringWithFormat:@"name %d", i]; user.password = [NSString stringWithFormat:@"password %d", i]; [dao addUserInMuliThread:user]; NSLog(@"writeDbOne %d ", i); } } } - (void)writeDbTwo { DbDao *dao = [DbDao sharedInstance]; for (int i = 600; i < 1200; i++) { @autoreleasepool { UserEntity *user = [[[UserEntity alloc] init] autorelease]; user.name = [NSString stringWithFormat:@"name %d", i]; user.password = [NSString stringWithFormat:@"password %d", i]; [dao addUserInMuliThread:user]; NSLog(@"writeDbTwo %d ", i); } } }
2014-08-22 19:45:17.987 FMDBThreadTest[2449:3507] 同步开始
2014-08-22 19:45:17.989 FMDBThreadTest[2449:3507] *****************sql--name 0--db<FMDatabase: 0x8fa22a0>
2014-08-22 19:45:17.999 FMDBThreadTest[2449:3507] 同步完成
2014-08-22 19:45:18.001 FMDBThreadTest[2449:3507] writeDbOne 0
2014-08-22 19:45:18.001 FMDBThreadTest[2449:3c03] 同步开始
2014-08-22 19:45:18.004 FMDBThreadTest[2449:3c03] *****************sql--name 600--db<FMDatabase: 0x8fa22a0>
2014-08-22 19:45:18.007 FMDBThreadTest[2449:3c03] 同步完成
2014-08-22 19:45:18.007 FMDBThreadTest[2449:3c03] writeDbTwo 600
2014-08-22 19:45:18.007 FMDBThreadTest[2449:4103] 同步开始
2014-08-22 19:45:18.086 FMDBThreadTest[2449:4103] 同步完成
2014-08-22 19:45:18.087 FMDBThreadTest[2449:3507] 同步开始
2014-08-22 19:45:18.087 FMDBThreadTest[2449:3507] *****************sql--name 1--db<FMDatabase: 0x8fa22a0>
2014-08-22 19:45:18.089 FMDBThreadTest[2449:3507] 同步完成
2014-08-22 19:45:18.090 FMDBThreadTest[2449:3507] writeDbOne 1
2014-08-22 19:45:18.090 FMDBThreadTest[2449:3c03] 同步开始
2014-08-22 19:45:18.091 FMDBThreadTest[2449:3c03] *****************sql--name 601--db<FMDatabase: 0x8fa22a0>
2014-08-22 19:45:18.093 FMDBThreadTest[2449:3c03] 同步完成
2014-08-22 19:45:18.097 FMDBThreadTest[2449:3c03] writeDbTwo 601
2014-08-22 19:45:18.097 FMDBThreadTest[2449:3507] 同步开始
2014-08-22 19:45:18.099 FMDBThreadTest[2449:3507] *****************sql--name 2--db<FMDatabase: 0x8fa22a0>
2014-08-22 19:45:18.101 FMDBThreadTest[2449:3507] 同步完成
2014-08-22 19:45:18.102 FMDBThreadTest[2449:3507] writeDbOne 2
2014-08-22 19:45:18.103 FMDBThreadTest[2449:3c03] 同步开始
2014-08-22 19:45:18.103 FMDBThreadTest[2449:3c03] *****************sql--name 602--db<FMDatabase: 0x8fa22a0>
2014-08-22 19:45:18.105 FMDBThreadTest[2449:3c03] 同步完成
由打印结果可知,在一个数据库操作没有执行之前,另外一个数据库操作是不会执行的。4、不重要,但是也不太确定的经验
每个线程分别操作自己的FMDatabase对象-----会导致数据库死锁
两个线程操作一个FMDatabase对象--in use
一个线程操作多个FMDatabase对象--(不科学)但是平常使用都用单例对象 ---估计是出于内存原因考虑
补充关于操作队列的认识:
根据cpu繁忙程度,自己分配线程,相同代码任务,不一定是同一个线程;
切记,也就是说如果操作到共有资源,注意只用同步锁;否则报些数据库 busy ,ID not 唯一的错,也是很烦人的。。。。
5、下面的情况需要使用同步锁
-(void)downAndWritePictureToLocalWithUrl:(NSString *)url { [self.lock lock]; NSString *imageName = [[[[url componentsSeparatedByString:@"/"] lastObject] componentsSeparatedByString:@"?"] firstObject]; if ([NDTResouceVerificationHelper shouldDownResourceInSubThread:imageName]) { NSURL *downImageURL = [NSURL URLWithString:url]; NSError *error; NSData *imageData = [NSData dataWithContentsOfURL:downImageURL options:NSDataReadingUncached error:&error]; if (error) { [self performSelectorOnMainThread:@selector(showAlert:) withObject:url waitUntilDone:YES]; }else { UIImage *image = [[UIImage alloc] initWithData:imageData]; [UIImageJPEGRepresentation(image, 1.0) writeToFile:[self getImagePath:imageName] atomically:YES]; //文件名称写入数据库 NDTResouceModel *resourceItem = [[NDTResouceModel alloc] init]; resourceItem.resouceID = url; resourceItem.resouceName = imageName; [[NDDatabase sharedDatabase] insertResourceItemInMultipleThread :resourceItem]; } } [self.lock unlock]; }上述代码中,downAndWritePictureToLocalWithUrl方法,在分线程中执行且有多个对象实例调用该方法,虽然2步数据库操作都在队列中执行,但是不能确定哪一步先放入队列,所以还是需要使用同步锁