To improve responsiveness, some synchronous methods that used FMDB to execute SQLite queries on the main thread were rewritten to be asynchronous, and run in the background via -performSelectorInBackground:withObject:
. SQLite not being thread-safe, however, each of these methods would ultimately call -[FMDatabase open]
, decreasing overall performance.
So, I wrote a proxy for the FMDB classes, which overrode -forwardInvocation:
to perform -[NSInvocation i开发者_JS百科nvokeWithTarget:]
on one specific thread, via -performSelector:onThread:withObject:waitUntilDone:
. This solved the problem of too many calls to -[FMDatabase open]
, but -forwardInvocation:
itself is rather expensive.
Is there a good way to solve this performance issue without rewriting all of the code that calls FMDB methods?
You've found the problem: don't call -performSelectorInBackground:withObject:
! There's no guarantee what it'll do, but it probably won't do the right thing.
If what you want is a single "database thread" for background ops, then there are several options:
- Create a new database thread and run loop and use
-performSelector:onThread:...
instead. - Create an NSOperationQueue with maxConcurrentOperationCount=1 and use NSOperation (NSInvocationOperation, perhaps?), or a serial dispatch queue. This isn't quite right: the operations/blocks are not guaranteed to be executed on the same thread, which might break sqlite (IIRC you can only move a DB handle between threads after freeing all statements)
- Use NSOperationQueue, but save a thread-local reference to the database in
[[NSThread currentThread] threadDictionary]
. This is a bit messy, since you have little control over when the database disappears. It also might violate the NSOperation contract (you're supposed to return the thread to its original state when the operation finishes).
精彩评论