我在我的应用程序开发中使用了惊人的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
我放在那里没有打印任何内容,应用程序没有崩溃并继续工作,但线程卡在那里,如果我关闭应用程序的运行,我再次重新启动它的线程不会停止在同一个任务,但随机在另一个,没有标准,一些时间一工作,有些时间不...任何人都可以帮忙吗?
答案 0 :(得分:9)
有几个问题向我发出,其中一个或多个问题可能导致您的问题:
我注意到你在本地创建了一个FMDatabaseQueue
对象。您应该只为整个应用程序共享一个FMDatabaseQueue
对象(我将它放在一个单例中)。数据库队列的目的是协调数据库交互,如果您在整个地方创建新的FMDatabaseQueue
对象,则无法合理地执行此操作。
我建议您不要在inDatabase
块中同步从网络下载一堆图像。
当您提交inDatabase
任务时,任何inDatabase
调用其他线程使用相同的FMDatabaseQueue
(并且他们应该使用相同的队列,否则您将失去其中的目的在你的操作中运行的那个之前不会继续(反之亦然)。
当从多个线程进行数据库交互时,由FMDatabaseQueue
串行队列协调,您确实希望尽快“进入并离开”。不要在inDatabase
块的中间嵌入潜在的慢速网络呼叫,否则所有其他数据库交互将被阻止,直到它完成。
所以,做一个inDatabase
来识别需要下载的图像,但就是这样。然后在inDatabase
调用之外,检索您的图像,如果您需要更新图像路径等,请单独inDatabase
调用以执行此操作。但是不要在inDatabase
块中包含任何缓慢和同步的内容。
我还注意到你在SELECT
表上执行task
,保持FMRecordSet
打开,然后尝试更新相同的记录。在尝试更新记录集中检索到的相同记录之前,您需要打开记录集,检索所需内容并关闭该记录集。
在尝试更新同一记录的FMResultSet
之前,请务必关闭executeUpdate
。
有点不相关,但我建议您考虑在原始img_url
语句中加入img_path
和SELECT
,这样您的字典条目数组就已经包含了所有内容需要它可以让你免于必须完成第二个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;