db executeUpdate …在FMDB块中并没有检查,没有错误

我在我的应用程序开发中使用了惊人的FMDB项目,我有一个像这样的NSOperation:

- (void)main { @autoreleasepool { FMDatabaseQueue *queue = [FMDatabaseQueue databaseQueueWithPath:[[NSUserDefaults standardUserDefaults] valueForKey:@"pathDB"]]; [queue inDatabase:^(FMDatabase *db) { FMResultSet *toQuery; if (self._id == nil) { toQuery = [db executeQuery:@"SELECT id,language,update_time FROM task"]; while ([toQuery next]) { [myarray addObject:[toQuery resultDictionary]]; } }]; for (int i = 0; i<[myarray count]; i++){ ...Do Something [queue inDatabase:^(FMDatabase *db) { FMResultSet *checkImgQuery = [db executeQuery:@"SELECT img_url,img_path FROM task WHERE id = ? AND language = ?",myTask.id ,myTask.lang]; while ([checkImgQuery next]) { if (![[checkImgQuery stringForColumn:@"img_url"] isEqualToString:myTask.img]) { NSData *my_img = [NSData dataWithContentsOfURL:[NSURL URLWithString:myTask.img]]; if (my_img != nil) { NSError *er; [my_img writeToFile:[checkImgQuery stringForColumn:@"img_path"] options:NSDataWritingAtomic error:&er]; //In the line under here the code block, the app still running, but this operation doesn't //go over this task [db executeUpdate:@"UPDATE task SET img_url = ? WHERE id = ? AND language = ?",myTask.img,[NSNumber numberWithInt:myTask.id],[NSNumber numberWithInt:myTask.language]; NSLog(@"%@",[db lastErrorMessage]); } ...Do Something } } } }]; } } 

问题是在[db executeUpdate:...] ,有时可以正常工作并且有时会冻结并且不会超过该行, NSLog我放在那里没有打印任何东西,应用程序不会崩溃并继续工作,但线程卡在那里,如果我关闭应用程序的运行,我再次重新启动线程不会停止在同一个任务,但随机在另一个,没有标准,一些时间工作,一些时间不…任何人都可以帮忙吗?

有几个问题从我这里跳出来,其中一个或多个问题可能导致您的问题:

  1. 我注意到你在本地创建了一个FMDatabaseQueue对象。 您应该只为整个应用程序共享一个FMDatabaseQueue对象(我将它放在一个单例中)。 数据库队列的目的是协调数据库交互,如果您在整个地方创建新的FMDatabaseQueue对象,则无法合理地执行此操作。

  2. 我建议不要使用inDatabase块来同步从网络下载一堆图像。

    当您提交inDatabase任务时,任何inDatabase在使用相同FMDatabaseQueue其他线程上调用(并且它们应该使用相同的队列,否则您在第一次使用队列时就会失败)在运行之前不会继续在你的操作中(反之亦然)。

    当从多个线程进行数据库交互时,由FMDatabaseQueue串行队列协调,您确实希望尽快“进入并离开”。 不要在inDatabase块的中间嵌入潜在的慢速网络调用,否则将阻止所有其他数据库交互,直到完成为止。

    因此,做一个inDatabase来识别需要下载的图像,但就是这样。 然后在inDatabase调用之外,检索您的图像,如果您需要更新图像路径等,单独的inDatabase调用就可以做到这一点。 但是不要在inDatabase块中包含任何慢速和同步的inDatabase

  3. 我还注意到你在task表上task SELECT ,保持FMRecordSet打开,然后尝试更新相同的记录。 在尝试更新记录集中检索到的同一记录之前,您需要打开记录集,检索所需内容并关闭该记录集。

    在尝试执行更新相同记录的executeUpdate之前,请始终关闭FMResultSet

  4. 有点不相关,但我可能会建议您考虑在原始SELECT语句中包含img_urlimg_path ,这样您的字典条目数组就已经拥有了您需要的所有内容,这样您就不必再完成第二个SELECT了。


如果您想知道FMDatabaseQueue单例可能是什么样子,您可能有一个DatabaseManager单例,其界面如下所示:

 // DatabaseManager.h @import Foundation; @import SQLite3; #import "FMDB.h" NS_ASSUME_NONNULL_BEGIN @interface DatabaseManager : NSObject @property (nonatomic, strong, readonly) FMDatabaseQueue *queue; @property (class, readonly, strong) DatabaseManager *sharedManager; - (id)init __attribute__((unavailable("Use +[DatabaseManager sharedManager] instead"))); + (id)new __attribute__((unavailable("Use +[DatabaseManager sharedManager] instead"))); @end NS_ASSUME_NONNULL_END 

并且实现可能如下所示:

 // DatabaseManager.m #import "DatabaseManager.h" @implementation DatabaseManager + (DatabaseManager *)sharedManager { static id sharedMyManager = nil; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ sharedMyManager = [[self alloc] init]; }); return sharedMyManager; } - (id)init { if ((self = [super init])) { _queue = [[FMDatabaseQueue alloc] initWithPath:[[NSUserDefaults standardUserDefaults] valueForKey:@"pathDB"]]; } return self; } @end 

然后,任何需要与数据库交互的代码都可以检索队列,如下所示:

 FMDatabaseQueue *queue = DatabaseManager.sharedManager.queue;