在App中保持一个FMDatabaseQueue
的实例,并在所有的线程中都只使用这一个实例。
<code class="language-objective-c hljs ini has-numbering" style="display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal; background: transparent;"><span class="hljs-title" style="margin: 0px; padding: 0px; box-sizing: border-box; color: rgb(0, 102, 102);">[FMDatabaseQueue databaseQueueWithPath:path]</span><span class="hljs-comment" style="margin: 0px; padding: 0px; color: rgb(136, 0, 0); box-sizing: border-box;">;</span></code><ul class="pre-numbering" style="margin: 0px; padding: 6px 0px 40px; box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="margin: 0px; padding: 0px 5px; box-sizing: border-box;">1</li></ul>
FMDatabaseQueue
虽然看似一个队列,实际上它本身并不是,它通过内部创建一个Serial的dispatch_queue_t
来处理通过inDatabase
和inTransaction
传入的Blocks,所以当我们在主线程(或者后台)调用inDatabase
或者inTransaction
时,代码实际上是同步的。FMDatabaseQueue
这么设计的目的是让我们避免发生并发访问数据库的问题,因为对数据库的访问可能是随机的(在任何时候)、不同线程间(不同的网络回调等)的请求。内置一个Serial队列后,FMDatabaseQueue
就变成线程安全了,所有的数据库访问都是同步执行,而且这比使用@synchronized
或NSLock
要高效得多。
但是这么一来就有了一个问题:如果后台在执行大量的更新,而主线程也需要访问数据库,虽然要访问的数据量很少,但是在后台执行完之前,还是会阻塞主线程。
对此,robertmryan给出了一些想法:
- 如果你是在后台使用的
inDatabase
来执行更新,可以考虑换成inTransaction
,后者比前者更新起来快很多,特别是在更新量比较大的时候(比如更新1000条或10000条)。 - 拆解你的更新数据量,如果有300条,可以分10次、每次更新30条。当然有时不能这么做,因为你可能通过网络请求回来的数据,你希望一次性、完整地写入到数据库中,虽然有局限性,不过这确实能很好地减少每个Block占用数据库的时间。
-
上面两点可以改善问题,但是问题依然是存在的,在大多数时候,你应该把从主线程调用inDatabase
和inTransaction
放在异步里:
<code class="language-objective-c hljs objectivec has-numbering" style="display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal; background: transparent;"><span class="hljs-built_in" style="margin: 0px; padding: 0px; color: rgb(102, 0, 102); box-sizing: border-box;">dispatch_async</span>(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, <span class="hljs-number" style="margin: 0px; padding: 0px; color: rgb(0, 102, 102); box-sizing: border-box;">0</span>), ^{
[<span class="hljs-keyword" style="margin: 0px; padding: 0px; color: rgb(0, 0, 136); box-sizing: border-box;">self</span><span class="hljs-variable" style="margin: 0px; padding: 0px; color: rgb(102, 0, 102); box-sizing: border-box;">.databaseQueue</span> inDatabase:^(FMDatabase *db) {
<span class="hljs-comment" style="margin: 0px; padding: 0px; color: rgb(136, 0, 0); box-sizing: border-box;">//do something...</span>
}];
});</code><ul class="pre-numbering" style="margin: 0px; padding: 6px 0px 40px; box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="margin: 0px; padding: 0px 5px; box-sizing: border-box;">1</li><li style="margin: 0px; padding: 0px 5px; box-sizing: border-box;">2</li><li style="margin: 0px; padding: 0px 5px; box-sizing: border-box;">3</li><li style="margin: 0px; padding: 0px 5px; box-sizing: border-box;">4</li><li style="margin: 0px; padding: 0px 5px; box-sizing: border-box;">5</li></ul>
这种方式能解决不依赖于数据库返回的结果的情况,如果对返回结果有依赖,就需要考虑UI上的体验了,如加一个UIActivityIndicatorView
。