Concept:
FMDB is the framework for data storage, which is the encapsulation of the SQLite database under the IOS platform. FMDB is Object-oriented , it encapsulates the C language API of SQLite in OC way, it is more convenient to use.
Core data is an ORM (object-relational mapping) embodiment of the use of core data need to transform the model, although the operation is simple, do not need to directly manipulate the database, but performance is not directly using SQLite high. But SQLite needs to use the C language function, the operation is more troublesome, so it needs to be encapsulated. But if it is simply encapsulated, it is likely to overlook many important details, such as how to handle concurrency and security issues.
Using the third-party framework Fmdb, which is the encapsulation of the libsqlite3 framework , uses the same steps as SQLite, and it handles multithreading while manipulating a table, which means it is thread-safe . Fmdb is a lightweight framework that is flexible to use and is the first choice for many enterprise development.
FMDB GitHub:
Important classes:
Fmresultset: Represents the result set after Fmdatabase executes the query .
Fmdatabase: Represents a single instance of a SQLite database operation used to execute SQL statements, which allows for the operation of the database for pruning and checking.
Fmdatabaseadditions: Extends the Fmdatabase class, adding the ability to simplify the way that query results return only a single value, whether a table, a column exists, a version number, a checksum SQL, and so on.
Fmdatabasequeue: A serial queue is used to support multi-threaded operations that perform multiple queries or updates in multiple threads, which are thread-safe .
Fmdatabasepool: Provides support for multithreading operations using the form of a task pool. (However, the official is not recommended to use this method, the preferred way to choose Fmdatabasequeue: Only_use_the_pool_if_you_are_doing_reads_otherwise_youll_deadlock_use_ Fmdatabasequeue_instead)
Fmdatabasequeue to use singleton creation, so that when multiple threads are called, database operations use a queue to ensure thread safety.
is to put the operation of the database into a serial queue to ensure that the database does not change at the same time.
The operation principle of using Fmdatabasequeue in multi-thread can create a management class to manage the access and delete of model data uniformly, can use the tool class operation, can also create the sub-class of integrated nsobject to manage, the model class that needs access inherits this subclass.
Fmdatabasequeue How to implement multithreading?
/** * Fmdatabasequeue How to implement multi-threaded case*/- (void) Fmdatabasequeuemutilthreadtest {//1. Get the database file pathNSString *doc =[Nssearchpathfordirectoriesindomains (NSDocumentDirectory, Nsuserdomainmask, YES) lastobject]; NSString*filename = [Doc stringbyappendingpathcomponent:@"Students.sqlite"]; //using queue1Fmdatabasequeue *queue1 =[Fmdatabasequeue Databasequeuewithpath:filename]; [Queue1 indatabase:^ (Fmdatabase *db) { for(intI=0; i<Ten; i++) {NSLog (@"queue1---%zi--%@", I,[nsthread CurrentThread]); } }]; Dispatch_async (Dispatch_get_global_queue (Dispatch_queue_priority_default,0), ^{[queue1 indatabase:^ (Fmdatabase *db) { for(intI= One; i< -; i++) {NSLog (@"queue1---%zi--%@", I,[nsthread CurrentThread]); } }]; }); Dispatch_async (Dispatch_get_global_queue (Dispatch_queue_priority_default,0), ^{[queue1 indatabase:^ (Fmdatabase *db) { for(intI= -; i< -; i++) {NSLog (@"queue1---%zi--%@", I,[nsthread CurrentThread]); } }]; }); //Although multiple threads are turned on, they can still be processed serially. The reasons are as follows: /**fmdatabasequeue Although it looks like a queue, in fact it is not, it through the internal creation of a serial dispatch_queue_t to handle through indatabase and intransaction incoming blocks, So when we call indatabase or intransaction in the main thread (or in the background), the code is actually synchronous. Fmdatabasequeue is designed so that we avoid the problem of concurrent access to the database, because access to the database can be random (at any time), different line threads (different network callbacks, etc.) requests. Once a serial queue is built in, Fmdatabasequeue becomes thread-safe, and all database accesses are performed synchronously, and this is much more efficient than using @synchronized or Nslock. */}
//虽然开启了多个线程,可依然还是串行处理。原因如下:
FMDatabaseQueue虽然看似一个队列,实际上它本身并不是,
它通过内部创建一个 Serial 的 dispatch_queue_t 来处理通过 inDatabase 和 inTransaction 传入的 Blocks.
所以当我们在主线程(或者后台)调用 inDatabase 或者 inTransaction 时,代码实际上是同步的。
FMDatabaseQueue这么设计的目的是让我们避免发生并发访问数据库的问题,因为对数据库的访问可能是随机的(在任何时候)、不同线程间(不同的网络回调等)的请求。
Although each queue is executed serially internally, it can be executed concurrently between different queues.
/** * Fmdatabasequeue How to implement multi-threaded case 2*/- (void) fmdatabasequeuemutilthreadtest2{//1. Get the database file pathNSString *doc =[Nssearchpathfordirectoriesindomains (NSDocumentDirectory, Nsuserdomainmask, YES) lastobject]; NSString*filename = [Doc stringbyappendingpathcomponent:@"Students.sqlite"]; //using queue1Fmdatabasequeue *queue1 =[Fmdatabasequeue Databasequeuewithpath:filename]; Dispatch_async (Dispatch_get_global_queue (Dispatch_queue_priority_default,0), ^{[queue1 indatabase:^ (Fmdatabase *db) { for(intI=0; i<5; i++) {NSLog (@"queue1---%zi--%@", I,[nsthread CurrentThread]); } }]; }); Dispatch_async (Dispatch_get_global_queue (Dispatch_queue_priority_default,0), ^{[queue1 indatabase:^ (Fmdatabase *db) { for(intI=5; i<Ten; i++) {NSLog (@"queue1---%zi--%@", I,[nsthread CurrentThread]); } }]; }); //using Queue2Fmdatabasequeue *queue2 =[Fmdatabasequeue Databasequeuewithpath:filename]; Dispatch_async (Dispatch_get_global_queue (Dispatch_queue_priority_default,0), ^{[Queue2 indatabase:^ (Fmdatabase *db) { for(intI=0; i<5; i++) {NSLog (@"queue2---%zi--%@", I,[nsthread CurrentThread]); } }]; }); Dispatch_async (Dispatch_get_global_queue (Dispatch_queue_priority_default,0), ^{[Queue2 indatabase:^ (Fmdatabase *db) { for(intI=5; i<Ten; i++) {NSLog (@"queue2---%zi--%@", I,[nsthread CurrentThread]); } }]; }); //new multiple queue operations the same one will not be guaranteed thread-safe. But it doesn't usually work that way. }
If the background is performing a large number of updates, and the main thread also needs to access the database, although the amount of data to be accessed is small, the main thread will still block until the background is finished executing. What to do?
Solution:
- If you are using in the background
inDatabase to perform the update, consider replacing inTransaction it, which is much faster than the former, especially if the update volume is relatively large (such as updating 1000 or 10,000).
- Remove your updated data volume, if there are 300, you can divide 10 times, update 30 each time. Of course, sometimes you can't do this, because you may be able to request the data back through the network, you want to write to the database in one-time, complete, although there are limitations, but it does a good job of reducing the time each block occupies the database.
- The above two points can improve the problem, but the problem is still there, and in most cases you should call it from the main thread
inDatabase and inTransaction put it in async:0), ^{ [self.databasequeue indatabase:^ (fmdatabase *db) { //do Something ... }];});
FMDB Source analysis of IOS development