2

我在开发中的应用程序中使用了令人惊叹的 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我放在那里的东西没有打印任何东西,应用程序不会崩溃并继续工作,但是线程卡在那里,如果我关闭应用程序的运行,然后我再次重新启动它,线程不会在同一个任务上停止,而是在另一个任务上随机停止,没有标准,有时一个工作,有时不......任何人都可以帮助?

4

1 回答 1

9

有几个问题突然出现在我身上,其中一个或多个可能会导致您的问题:

  1. 我注意到您正在FMDatabaseQueue本地创建一个对象。你应该只FMDatabaseQueue为整个应用程序共享一个对象(我把它放在一个单例中)。FMDatabaseQueue数据库队列的目的是协调数据库交互,如果您在各处创建新对象,它就无法合理地做到这一点。

  2. 我建议不要有一个inDatabase块,您可以在其中从网络同步下载一堆图像。

    当您提交inDatabase任务时,使用相同任务的其他线程上的任何inDatabase调用FMDatabaseQueue(并且它们应该使用相同的队列,否则您将违背首先拥有队列的目的)在您的操作中运行之前不会继续确实(反之亦然)。

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

    所以,做一个inDatabase识别需要下载的图像,仅此而已。然后在inDatabase通话之外,检索您的图像,如果您需要更新图像路径等,请单独inDatabase调用来执行此操作。但是不要在inDatabase块内包含任何缓慢和同步的东西。

  3. 我还注意到您正在做一个桌面,保持SELECT打开状态,然后尝试更新相同的记录。在尝试更新在记录集中检索到的相同记录之前,您希望打开记录集、检索所需内容并关闭该记录集。taskFMRecordSet

    FMResultSet在尝试executeUpdate更新同一记录之前,请始终关闭。

  4. 有点不相关,但我可能会建议您考虑在原始语句中包含img_urland ,这样您的字典条目数组将已经拥有您需要的一切,并且完全可以避免您必须这样做。img_pathSELECTSELECT


如果您想知道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;
于 2013-08-13T18:46:44.457 回答