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 つまたは複数が問題の原因となっている可能性があります。

  1. FMDatabaseQueueローカルでオブジェクトを作成していることに気付きました。FMDatabaseQueueアプリ全体で共有するオブジェクトは 1 つだけにする必要があります (私はそれをシングルトンに入れました)。FMDatabaseQueueデータベース キューの目的は、データベースのやり取りを調整することですが、あちこちに新しいオブジェクトを作成している場合、それを行うことは合理的ではありません。

  2. inDatabaseネットワークから大量の画像を同期的にダウンロードするブロックを作成しないことをお勧めします。

    inDatabaseタスクを送信するとinDatabase、同じものを使用する他のスレッドでの呼び出しFMDatabaseQueue(同じキューを使用する必要があります。そうしないと、最初にキューを持つ目的が無効になります) は、操作で実行されているタスクが実行されるまで続行されません。します (またはその逆)。

    シリアル キューによって調整された複数のスレッドからデータベースとのやり取りを行う場合FMDatabaseQueue、できる限り迅速に「出入り」できるようにする必要があります。低速になる可能性のあるネットワーク コールをブロックの途中に埋め込まないでください。そうしないとinDatabase、他のすべてのデータベース インタラクションが完了するまでブロックされます。

    したがって、inDatabaseダウンロードする必要があるイメージを特定するために を実行しますが、それだけです。次に、inDatabase呼び出しの外で画像を取得し、画像のパスなどを更新する必要がある場合は、inDatabasedo を個別に呼び出してそれを行います。inDatabaseただし、ブロック内に低速で同期的なものを含めないでください。

  3. SELECTまた、taskテーブルをFMRecordSet開いたままにして、同じレコードを更新しようとしていることに気付きました。レコード セットで取得した同じレコードを更新する前に、レコード セットを開いて必要なものを取得し、そのレコードセットを閉じます。

    同じレコードを更新するを実行する前に、必ず を閉じて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 に答える