Wie Sie richtig nennen SQLite-Funktionen aus hintergrund-thread auf dem iPhone?
Verwende ich eine SQLite Datenbank in meinem iPhone app. Beim Start, es gibt einige Datenbank-Aktionen, die ich ausführen möchten, in einem separaten thread. (Ich mache dies vor allem auf Minimierung der startup-Zeit.)
Gelegentlich/zufällig, wenn diese Datenbank-Aufrufe aus dem hintergrund-thread, der die app zum Absturz, mit diesen Fehlern:
2009-04-13 17:36:09.932 Action Lists[1537:20b] *** Assertion failure in -[InboxRootViewController getInboxTasks], /Users/cperry/Dropbox/Projects/iPhone GTD/GTD/Classes/InboxRootViewController.m:74
2009-04-13 17:36:09.932 Action Lists[1537:3d0b] *** Assertion failure in +[Task deleteCompletedTasksInDatabase:completedMonthsAgo:], /Users/cperry/Dropbox/Projects/iPhone GTD/GTD/Classes/Data Classes/Task.m:957
2009-04-13 17:36:09.933 Action Lists[1537:20b] *** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Error: failed to prepare statement with message 'library routine called out of sequence'.'
2009-04-13 17:36:09.933 Action Lists[1537:3d0b] *** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Error: failed to prepare statement with message 'library routine called out of sequence'.'
Zwar kann ich nicht zuverlässig reproduzieren den Fehler, ich habe mich davon überzeugt, dass es aufgrund der Tatsache, dass SQLite-Funktionen sind dazu aufgerufen, in den beiden aktiven threads. Wie sollte ich rufe SQLite-Funktionen von einem separaten thread? Gibt es einen trick, ich bin fehlt? Ich bin ziemlich neu auf dem iPhone, SQLite, Objective-C, so dass es sein könnte etwas, das offensichtlich zu Ihnen, aber nicht so obviouse zu mir.
Hier sind einige code-Beispiele.
MainApplication.m:
- (void)applicationDidFinishLaunching:(UIApplication *)application {
//Take care of jobs that have to run at startup
[NSThread detachNewThreadSelector:@selector(startUpJobs) toTarget:self withObject:nil];
}
//Jobs that run in the background at startup
- (void)startUpJobs {
//Anticipating that this method will be called in its own NSThread, set up an autorelease pool.
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
//Get user preferences
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
//This Class Method calls SQLite functions and sometimes causes errors.
[Task revertFutureTasksStatus:database];
[pool release];
}
Aufgabe.m:
static sqlite3_stmt *revert_future_statement = nil;
+ (void) revertFutureTasksStatus:(sqlite3 *)db {
if (revert_future_statement == nil) {
//Find all tasks that meet criteria
static char *sql = "SELECT task_id FROM tasks where ((deleted IS NULL) OR (deleted=0)) AND (start_date > ?) AND (status=0) AND (revert_status IS NOT NULL)";
if (sqlite3_prepare_v2(db, sql, -1, &revert_future_statement, NULL) != SQLITE_OK) {
NSAssert1(0, @"Error: failed to prepare update statement with message '%s'.", sqlite3_errmsg(db));
}
}
//Bind NOW to sql statement
NSDate *now = [[NSDate alloc] init];
NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
[formatter setDateFormat:@"yyyy-MM-dd"];
NSString *nowString = [formatter stringFromDate:now];
sqlite3_bind_text(revert_future_statement, 1, [nowString UTF8String], -1, SQLITE_TRANSIENT);
[now release];
[formatter release];
//We "step" through the results - once for each row.
while (sqlite3_step(revert_future_statement) == SQLITE_ROW) {
//Do things to each returned row
}
//Reset the statement for future reuse.
sqlite3_reset(revert_future_statement);
}
InformationsquelleAutor ceperry | 2009-04-13
Du musst angemeldet sein, um einen Kommentar abzugeben.
Fehlermeldung Karten zu SQLITE_MISUSE (der source code ist verfügbar unter http://www.sqlite.org).
Sehen http://www.sqlite.org/faq.html#q6 für die Einschränkungen auf die Verwendung einer sqlite3 * database Griff aus mehr als einem thread. Effektiv, Sie erlaubt die Wiederverwendung eines Datenbank-handle und Aussagen über threads, aber ein thread sein muss, ist völlig fertig auf die Datenbank zugreifen, bevor der andere thread startet (d.h. überlappende Zugriff ist nicht sicher). Das klingt wie das, was passiert für Sie und steht im Einklang mit der SQLITE_MISUSE Fehlercode.
Wenn Sie brauchen, um Zugriff auf die gleiche Datenbank aus mehr als einem thread, empfehle ich stattdessen öffnen Sie die Datenbank, die separat von jedem Faden und setzen Sie ein Zeitlimit mit sqlite3_busy_timeout(). Sqlite wird dann Griff Anstoßes für Sie, blockieren für eine kurze Zeit in einem thread, wenn der andere thread ist Daten schreiben, während immer noch erlaubt die gleichzeitige liest.
InformationsquelleAutor dmercredi
Habe ich versucht, diese beiden Lösungen und Sie funktionierte perfekt. Sie können entweder kritische Abschnitte oder NSOperationQueue und ich bevorzuge die erste, hier ist der code für beide von Ihnen:
definieren einer Klasse "DatabaseController" und fügen Sie diesen code in Ihre Umsetzung:
ODER verwenden Sie den NSOperationQueue die Sie verwenden können:
diese beiden Lösungen blockiert den aktuellen thread, bis das schreiben in die Datenbank ist fertig, die man berücksichtigen kann, in den meisten Fällen.
you rock! die erste Lösung ist einfach... unglaublich!
InformationsquelleAutor Mousa
SQLite Griffe (
sqlite3_stmt *
sicher, undsqlite3 *
glaube ich) sind thread-spezifisch. Die korrekte Weise, Sie zu nennen, die von mehreren threads ist, dass eine separate Reihe von Griffen für jeden thread.InformationsquelleAutor Marco
Wenn Sie möchten, verwenden Sie SQLite auf mehrere threads ohne Einschränkungen, führen Sie die folgenden vor dem öffnen Verbindung:
sqlite3_shutdown();
sqlite3_config(SQLITE_CONFIG_SERIALIZED);
sqlite3_initialize();
http://www.sqlite.org/threadsafe.html
InformationsquelleAutor Harris
Ich würde NSOperation und nur tun, was es alles gibt während des Startvorgangs. NSOperation Felsen. Habe ich gesagt, wie viel NSOperation Felsen? Es funktioniert. Rock, dass ist.
InformationsquelleAutor Genericrich
Wenn Sie noch nicht haben viel Glück mit höher, Sie könnten versuchen, mit diesem wrapper EnormEGO
https://github.com/jdp-global/egodatabase
Verwenden Sie asynchrone Rückrufe, die können töten zwei Vögel mit einem Stein.
Haben Sie einen Blick auf meine Readme-Sektion für
EGODatabaseRequest - asynchrone requests zur db
InformationsquelleAutor johndpope
Ihre beste Wette ist die Verwendung von GCD (Grand Central Dispatch) - Warteschlangen, um zu verhindern, dass der gleichzeitige Zugriff auf die sqlite-Datenbank.
Mit irgendeiner form von sperren (einschließlich der Datei zu sperren, wäre von mehreren Datenbank-Instanzen), kann zu busy waiting, das ist verschwenderisch.
Sehen meine Antwort zu einer ähnlichen Frage.
InformationsquelleAutor
Meine Lösung ist das löschen der app auf meinem Gerät (ich möchte, um die Datenbank zu löschen, die ich erstellt haben). Dies löst das problem für mich.
InformationsquelleAutor Nguyen Thap