2011-04-25 11 views
7

Używam opakowanie FMDatabase SQLite w Objective C i mam następujący problem:SQLite Database z FMDatabase Wrapper

Używam się parsować XML i wstawić DB w wątku tło dla niektórych treści, które użytkownik nie dostęp do pracy, jednak użytkownik jest w stanie współdziałać z interfejsu użytkownika i bazy danych z sekcji one się znajdują.

The FMDatabase <FMDatabase: 0x17b7b0> is currently in use. 

Losowo, dostanę „FMDatabase już w użyciu” powiadomienia i tablica nie zostanie wypełnione przez bazę danych. Byłem pod wrażeniem, że klasa FMDatabase będzie obsługiwać zapytania raz stało się wolne, ale mam:

while(contents.count < 1){ 
    sleep(1); 
} 

Mając nadzieję, że gdy baza danych zwalnia, tablica zostanie wypełniona. Próbowałem również ponownie uruchomić skrypt populacji tablicy, jeśli DB jest zajęty, ale bez skutku.

Przepraszam, jeśli to pytanie jest mylące, z przyjemnością wyjaśniam.

Odpowiedz

7

Doświadczyłem tego samego problemu.

Przełączyłem na FMDatabaseQueue dla każdego zapytania/aktualizacji bazy danych, które musiałem zrobić. To działa jak urok !

Korzystanie z jest dobrym pomysłem, ale jeśli chodzi o faktyczne kodowanie, może być dość trudnym przekazanie argumentów i uzyskanie wyników do dalszego wykorzystania.

EDIT: tutaj jest mały przykład

-(void) updateTaskStateAsync:(NSNumber *)taskID withNewStatus:(NSNumber *)state andCompletionBlock:(void(^)(BOOL status))completionBlock{ 

    NSString *errInfo = [NSString stringWithFormat:@"taskID %d - state %d", [taskID intValue], [state intValue]]; 

    [queue inDatabase:^(FMDatabase *db) { 
     BOOL r = [db executeUpdate:@"UPDATE tasks SET state=?, date_job_last_updated=? WHERE identifier=?", state, [NSDate dateWithTimeIntervalSinceNow:0], taskID]; 
     DB_DISPLAY_ERROR(errInfo); // convenient macro to log errors 

     if(completionBlock) 
      completionBlock(r); 
    }]; 
} 

-(BOOL) updateTaskStateSync:(NSNumber *)taskID withNewStatus:(NSNumber *)state { 

    NSString *errInfo = [NSString stringWithFormat:@"taskID %d - state %d", [taskID intValue], [state intValue]]; 
    __block BOOL r = NO; 
    __block BOOL ended = NO; 

    [queue inDatabase:^(FMDatabase *db) { 
     r = [db executeUpdate:@"UPDATE tasks SET state=?, date_job_last_updated=? WHERE identifier=?", state, [NSDate dateWithTimeIntervalSinceNow:0], taskID]; 
     DB_DISPLAY_ERROR(errInfo); // convenient macro to log errors 

     ended = YES; 
    }]; 

    NSCondition *cond = [[NSCondition alloc] init]; 
    [cond lock]; 
    while(!ended) 
     [cond wait]; 

    [cond unlock]; 

    return r; 
} 
+0

chcę zrobić to samo jak wy, ale dla zapytania w FMDatabaseQueue, wynik musi być powrót asynchroniczny ..... to nie jest bardzo wygodne. Czy masz jakieś propozycje? – flypig

+0

Nie mam tego problemu. Zmieniłem swoją odpowiedź, aby dodać fragment kodu, proszę załączyć, jeśli to nie pomoże. – dvkch

+0

kod blokowy jest wykonywany przez kolejkę fmdb asynchronicznie, a r będzie aktualizowany w bloku, gdy zostanie wykonany kod bloku, ale po prostu zwrócisz r w ostatnim wierszu procedury ... myślę, że dostaniesz pusty wynik zapytania czasami, w zależności od statusu kolejki fmdb. To mnie zdezorientowało przez kilka dni ... może powinniśmy przejść blok ukończenia, aby zwrócić wynik zapytania, np. "(BOOL) updateTaskState: (NSNumber *) taskID withNewStatus: (NSNumber *) state state: (void (^) (BOOL r)) complete;" i wywołaj zakończenie (r) w [queue inDatabase:^(FMDatabase * db) blok {..}]. – flypig

4

Trafiasz w ten problem, ponieważ twoja aplikacja jest wielowątkowa i uzyskujesz dostęp do tej samej bazy FMD z różnych wątków. Podobne pytanie, ale dla Pythona, można znaleźć here.

FMDatabase to otoki wokół interfejsu API sqlite. sqlite domyślnie nie zezwala na współbieżność, więc baza danych FMDatabase wykorzystuje zmienną składową, zwaną "inUse", do śledzenia. Aby rozwiązać problem, spróbuj jednej z tych metod zdefiniowanych w NSObject, aby wszystkie wywołania do FMDatabase wystąpiły w tym samym wątku.

  • performSelectorOnMainThread: withObject: waitUntilDone:
  • performSelectorOnMainThread: withObject: waitUntilDone: tryby:
  • performSelector: onThread: withObject: waitUntilDone:
  • performSelector: onThread: withObject: waitUntilDone: Tryby: