iOS 開發之 FMDB 源碼分析

來源:互聯網
上載者:User

標籤:use   管理   案例   uem   mask   網路   完整   版本   操作   

  概念:

  FMDB 是用於資料存放區的架構,它是 iOS 平台下對 SQLite 資料庫的封裝。FMDB 是物件導向的,它以 OC 的方式封裝了 SQLite 的 C 語言 API,使用起來更加方便。

  Core Data是 ORM(對象關係映射) 的一種體現,使用Core Data需要用到模型資料的轉化,雖然操作簡單,不需要直接操作資料庫,但是效能沒有直接使用SQLite高。但是SQLite使用的時候需要使用c語言中的函數,操作比較麻煩,因此需要對它進行封裝。但是如果只是簡單地封裝,很可能會忽略很多重要的細節,比如如何處理並發以及安全性更問題。

  使用第三方架構FMDB,它是對libsqlite3架構的封裝,用起來的步驟與SQLite使用類似,並且它對於多線程的同時操作一個表格時進行了處理,也就意味著它是安全執行緒的。FMDB是輕量級的架構,使用靈活,它是很多企業開發的首選。

  FMDB GitHub:

 

  重要的類:
  1. FMResultSet : 表示FMDatabase執行查詢之後的結果集。

  2. FMDatabase : 表示一個單獨的SQLite資料庫操作執行個體,用來執行SQL語句, 通過它可以對資料庫進行增刪改查等等操作。

  3. FMDatabaseAdditions : 擴充FMDatabase類,新增對查詢結果只返回單個值的方法進行簡化,對錶、列是否存在,版本號碼,校正SQL等等功能。

  4. FMDatabaseQueue : 使用串列隊列 ,對多線程的操作進行了支援,用於在多線程中執行多個查詢或更新,它是安全執行緒的。

  5. FMDatabasePool : 使用任務池的形式,對多線程的操作提供支援。(不過官方對這種方式並不推薦使用,優先選擇FMDatabaseQueue的方式:ONLY_USE_THE_POOL_IF_YOU_ARE_DOING_READS_OTHERWISE_YOULL_DEADLOCK_USE_FMDATABASEQUEUE_INSTEAD)

  FMDatabaseQueue 要使用單例建立,這樣多線程調用時,資料庫操作使用一個隊列,保證安全執行緒。

  是把資料庫的操作放到一個串列隊列中,從而保證不會在同一時間對資料庫做改動。

  多線程下使用FMDatabaseQueue的操作原理就可以建立一個管理類對模型資料的存取查刪進行統一管理,可以使用工具類操作,也可以建立整合NSObject的子類進行管理,需要存取的模型類繼承此子類即可。

  FMDatabaseQueue如何?多線程?

/** *  FMDatabaseQueue如何?多線程的案例 */- (void)FMDatabaseQueueMutilThreadTest {    //1、擷取資料庫檔案路徑    NSString *doc = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];    NSString *fileName = [doc stringByAppendingPathComponent:@"students.sqlite"];    //使用queue1    FMDatabaseQueue *queue1 = [FMDatabaseQueue databaseQueueWithPath:fileName];    [queue1 inDatabase:^(FMDatabase *db) {        for (int i=0; i<10; i++) {            NSLog(@"queue1---%zi--%@",i,[NSThread currentThread]);        }    }];        dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{        [queue1 inDatabase:^(FMDatabase *db) {            for (int i=11; i<20; i++) {                NSLog(@"queue1---%zi--%@",i,[NSThread currentThread]);            }        }];    });        dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{        [queue1 inDatabase:^(FMDatabase *db) {            for (int i=20; i<30; i++) {                NSLog(@"queue1---%zi--%@",i,[NSThread currentThread]);            }        }];    });        //雖然開啟了多個線程,可依然還是串列處理。原因如下:        /**FMDatabaseQueue雖然看似一個隊列,實際上它本身並不是,它通過內部建立一個Serial的dispatch_queue_t來處理通過inDatabase和inTransaction傳入的Blocks,所以當我們在主線程(或者後台)調用inDatabase或者inTransaction時,代碼實際上是同步的。FMDatabaseQueue這麼設計的目的是讓我們避免發生並發訪問資料庫的問題,因為對資料庫的訪問可能是隨機的(在任何時候)、不同線程間(不同的網路回調等)的請求。內建一個Serial隊列後,FMDatabaseQueue就變成安全執行緒了,所有的資料庫訪問都是同步執行,而且這比使用@synchronized或NSLock要高效得多。     */}

  

  //雖然開啟了多個線程,可依然還是串列處理。原因如下:
  FMDatabaseQueue雖然看似一個隊列,實際上它本身並不是,
  它通過內部建立一個 Serial 的 dispatch_queue_t 來處理通過 inDatabase 和 inTransaction 傳入的 Blocks.
  所以當我們在主線程(或者後台)調用 inDatabase 或者 inTransaction 時,代碼實際上是同步的。
  FMDatabaseQueue這麼設計的目的是讓我們避免發生並發訪問資料庫的問題,因為對資料庫的訪問可能是隨機的(在任何時候)、不同線程間(不同的網路回調等)的請求。
  內建一個 Serial 隊列後,FMDatabaseQueue 就變成安全執行緒了,所有的資料庫訪問都是同步執行,而且這比使用 @synchronized 或 NSLock要高效得多。

  雖然每個queue內部是串列執行的,當時不同的queue之間可以並發執行。

/** *  FMDatabaseQueue如何?多線程的案例2 */- (void)FMDatabaseQueueMutilThreadTest2{    //1、擷取資料庫檔案路徑    NSString *doc = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];    NSString *fileName = [doc stringByAppendingPathComponent:@"students.sqlite"];        //使用queue1    FMDatabaseQueue *queue1 = [FMDatabaseQueue databaseQueueWithPath:fileName];        dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{        [queue1 inDatabase:^(FMDatabase *db) {            for (int i=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 (int i=5; i<10; i++) {                NSLog(@"queue1---%zi--%@",i,[NSThread currentThread]);            }        }];    });        //使用queue2    FMDatabaseQueue *queue2 = [FMDatabaseQueue databaseQueueWithPath:fileName];        dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{        [queue2 inDatabase:^(FMDatabase *db) {            for (int i=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 (int i=5; i<10; i++) {                NSLog(@"queue2---%zi--%@",i,[NSThread currentThread]);            }        }];    });        //建立多個隊列操作同一個 就不發保證安全執行緒了。不過一般 不會這麼用。}

  

  如果後台在執行大量的更新,而主線程也需要訪問資料庫,雖然要訪問的資料量很少,但是在後台執行完之前,還是會阻塞主線程。 怎麼辦?

  解決方案:

  1. 如果你是在後台使用的 inDatabase 來執行更新,可以考慮換成 inTransaction,後者比前者更新起來快很多,特別是在更新量比較大的時候(比如更新1000條或10000條)。
  2. 拆解你的更新資料量,如果有300條,可以分10次、每次更新30條。當然有時不能這麼做,因為你可能通過網路請求回來的資料,你希望一次性、完整地寫入到資料庫中,雖然有局限性,不過這確實能很好地減少每個Block佔用資料庫的時間。
  3. 上面兩點可以改善問題,但是問題依然是存在的,在大多數時候,你應該把從主線程調用 inDatabase 和 inTransaction 放在非同步裡:
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{    [self.databaseQueue inDatabase:^(FMDatabase *db) {        //do something...    }];});

     

  

 

iOS 開發之 FMDB 源碼分析

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.