3

this SO questionに関連して、データの読み込みをバックグラウンドに置きたいと思います。

ただし、「ライブラリ ルーチンの呼び出し順序が間違っています」というエラーが表示されます。

このSOスレッドでは、方法はNSOperationを使用していると言っていますが、Web上のサンプルを見ると、問題を解決する方法がわかりません。

シングルトン パターンと単一の sqlite 接続を共有します。

@interface Db : NSObject {
    NSString *path;
    FMDatabase* theDb;
    BOOL isOpen;
}

@property (retain, nonatomic) FMDatabase *theDb;
@property (retain, nonatomic) NSString *path;
@property (nonatomic) BOOL isOpen;
--------
static Db *currentDbSingleton = nil;
#pragma mark Global access

+(id)currentDb {
    @synchronized(self) {
        if (!currentDbSingleton) {
            NSString *reason = NSLocalizedString(@"The database is not set globally",
                                                 @"Error Db: database is not set");
            NSException *e = [NSException exceptionWithName:@"DBError"                        
                                                     reason:reason;
                                                   userInfo:nil];
            @throw e;
        }
    }
    return currentDbSingleton;  
}

同じデータベースを2回開くのは難しいです....

何か案は?

編集:

エラーがsqliteの呼び出しにあることを確認しました。それを呼び出すための薄いラッパーとして FDBM を使用します。

データをロードするためのメイン タスクとバックグラウンド タスクの 2 つのスレッドを実行しています。私はこのように実行します:

- (void) fillCache:(NSString *)theTable {
    [NSThread detachNewThreadSelector:@selector(fillCacheBackground:)
                             toTarget:self
                           withObject:theTable];
}

- (void)loadComplete {
    [self.table reloadData];
}

- (void) fillCacheBackground:(NSString *)theTable {
    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
    Db *db= [Db currentDb];
    [db beginTransaction];
        ..... STUFF HERE
    [db commitTransaction];
    //Tell our callback what we've done
    [self performSelectorOnMainThread:@selector(loadComplete) 
                           withObject:nil 
                        waitUntilDone:YES];
    [pool drain];
}

db インターフェースのコードはhttp://code.google.com/p/chibiorm/source/browse/#svn/trunk/srcにあります。具体的には、fdbm/ とインターフェースする唯一のユニットである Db.h/m です。 sqlite。

FDBM から sqlite 関数を呼び出そうとすると、エラーが発生しました。

たとえば、ここで起こりました:

-(void) checkError {
    if ([self.theDb hadError]) { // <====ERROR HERE
        NSLog(@"Err %d: %@", [self.theDb lastErrorCode], [self.theDb]);
    }
}

これは FDBM コードを呼び出します。

- (BOOL) hadError {
    int lastErrCode = sqlite3_errcode(db);
    return (lastErrCode > SQLITE_OK && lastErrCode < SQLITE_ROW);
}
4

2 に答える 2

3

シングルトン メソッドは良いアイデアですが、実際にはどこでも初期化しているようには見えませんcurrentDbSingleton...それを修正して有効な DB 接続を返すと仮定すると、それは問題ではないと思います。

あなたが言及した「ライブラリルーチンが順番外で呼び出された」エラーは、使用しているライブラリ(SQLiteまたはFMDB)がメソッド/関数呼び出しが特定の順序で行われることを期待していることを私に知らせます。あなたが抱えている可能性があるのは、2つ以上のスレッドが同じライブラリを呼び出している同時実行の問題であり、それぞれが正しい順序を使用している可能性がありますが、「同時に話している」場合、いわば、ライブラリが受け取る可能性があります予期されたシーケンスから呼び出します。あなたが望むのは、呼び出しのセットがアトミック単位として扱われ、それらが重複したり混ざり合ったりしないようにすることです。

ここでNSOperation の出番です。それをサブクラス化すると、一連のコードを「単一のカプセル化されたタスク」として扱うことができます。おそらく、非並行操作用に設計する必要があります。クラスのドキュメントには、NSOperation 内でロジックを実装する方法を説明する詳細が含まれています。


編集:(質問者が追加のコンテキストで質問を明確にした後)

問題は で発生してcheckErrorおり、そのメソッドはリンクされた .m ファイルの複数の場所から呼び出さhadErrorれているため、間違った時間に呼び出している可能性が高くなります。(トランザクションが閉じられた後に呼び出されますか? 次のトランザクションが開始された後に呼び出された場合はどうなりますか?)

fillCacheたとえば、前の呼び出しがまだデータベースにアクセスしているときに が呼び出された場合はどうなるでしょうか。この点で、あなたのトランザクション管理方法は非常に疑わしいように見えます。たとえば、誰かがbeginTransactionand inTransactionisを呼び出した場合YES、メソッドは何もせずに戻ります (つまり、FMDatabase ivar をまったく呼び出しません)。おそらく必要なのは、最初の呼び出し元がトランザクションを終了するまで 2 番目の呼び出し元を待機させることです。(ただし、FMDatabase が同時トランザクションをサポートしている場合を除きます。その場合はbeginTransaction、関係なく呼び出す必要があります。)

まだお読みでない場合は、Objective-C スレッド同期に関する Apple の記事をお読みください。NSLock次に、 (特にlockBeforeDate:) および関連するサンプル コードのドキュメントを参照してください。メソッド内で、-[Db beginTransaction]おそらくロックの取得をブロックしたいと思うでしょう。

+allocWithZone など、いくつかの固有のクラス メソッドもあります。 — 可能であれば +inizialize (クラスが最初に参照されたときにランタイムによって自動的に呼び出される) を使用することを選択すると、クラスは必要なく自身を初期化することができます。手動呼び出しの場合。(+alloc を呼び出してから -initWithName: を呼び出し、それを +setCurrentDb にフィードバックすると思います。すべてを処理する +initializeWithPath: などの便利なメソッドを使用すると、はるかにクリーンになります。)

+setCurrentDb: は、トランザクションが処理中かどうかに関係なくシングルトン オブジェクトをスワップ アウトできる (そして古いシングルトンは解放されない) という事実など、他にも多くの落とし穴があります。シングルトン インスタンスなど。ただし、直面している最大の問題は、同時実行性を正しくすることです。FMDatabase 参照を保護するためにロックを実装することは正しい方向への一歩だと思いますが、メソッド X を NSOperation でラップするだけではそれができません。theDb他の誰も参照していないという保証なしに参照するコード内のすべてのポイントは、クラッシュの危険があります。これが難しいように見えても、気にしないでください。

最後のランダムな提案: メソッドTypeForField:Type:をとValueForField:Name:Type:にそれぞれ変更します。正確さ、読みやすさ、および一致する慣習に努めます。typeForFieldName:typeName:valueForResultSet:fieldName:typeName:

于 2009-07-06T15:51:41.240 に答える
2

私はついに実用的な解決策を見つけました。

アイデアは、接続のデータベースプールを構築し、データベースを2回開くことです。1 つの接続をメイン スレッドに使用し、別の接続をバッ​​クグラウンドに使用します。

すべてがhttp://code.google.com/p/chibiorm/になりました

于 2010-07-24T17:09:49.650 に答える