ios多線程環境下數據庫架構


      在IOS程序設計中,一般都需要管理本地化數據。apple為我們提供多種方式來本地化數據比如:core data,一般的平面文件,當然還有sqlite。core data在蘋果的官方文檔中說是一個高級功能,不建議新手程序員使用。我粗略的研究了一下core data,畢竟是蘋果原生的東西。在很多地方,與蘋果原生的系統結合的非常好。比如可以直接將core data作為UITableView的數據源來使用,無論是從編程效率還是程序的優雅型上提高了很多。但是,正如蘋果說的那樣這畢竟是一項高級功能,在使用起來還是有點費勁的,尤其是在多線程環境下。因為使用管了sqlite,所以在本地化的時候就多看了一下sqlite的東西。
      apple只提供了sqlite的庫文件,如果想使用sqlite,還是得自己再去封裝一層sqlite。開始,我用C++封裝了簡單的封裝了一層數據庫,方便上層調用。但是在使用的過程中越到了多線程的問題。sqlite在多線程環境下有三種運行模式:參見(http://blog.csdn.net/diyagoanyhacker/article/details/7209888)
單線程:禁用所有的mutex鎖,並發使用時會出錯。當SQLite編譯時加了SQLITE_THREADSAFE=0參數,或者在初始化SQLite前調用sqlite3_config(SQLITE_CONFIG_SINGLETHREAD)時啟用。 
多線程:只要一個數據庫連接不被多個線程同時使用就是安全的。源碼中是啟用bCoreMutex,禁用bFullMutex。實際上就是禁用數據庫連接和prepared statement(准備好的語句)上的鎖,因此不能在多個線程中並發使用同一個數據庫連接或prepared statement。當SQLite編譯時加了SQLITE_THREADSAFE=2參數時默認啟用。若SQLITE_THREADSAFE不為0,可以在初始化SQLite前,調用sqlite3_config(SQLITE_CONFIG_MULTITHREAD)啟用;或者在創建數據庫連接時,設置SQLITE_OPEN_NOMUTEX flag。 
串行:啟用所有的鎖,包括bCoreMutex和bFullMutex。因為數據庫連接和prepared statement都已加鎖,所以多線程使用這些對象時沒法並發,也就變成串行了。當SQLite編譯時加了SQLITE_THREADSAFE=1參數時默認啟用。若SQLITE_THREADSAFE不為0,可以在初始化SQLite前,調用sqlite3_config(SQLITE_CONFIG_SERIALIZED)啟用;或者在創建數據庫連接時,設置SQLITE_OPEN_FULLMUTEX flag。、
     蘋果提供的庫在測試后發現,他們在編譯的時候使用的是第二種方式。第二種方式,解決多個線程讀寫同一個數據庫的問題。但是同一個數據庫連接不能夠多個線程間共享。一個解決方案是為每一個線程都生成一個數據庫連接。於是得頻繁的打開和關閉數據庫連接。雖然打開和關閉sqlite消耗的時間並不是很大,但是每次打開都會系統會為這個數據庫連接開辟一段內存作為緩存,但是關閉庫連接的時候並沒有回收(回收機制不太明確)。而蘋果建議手機程序占用內存不能超過20MB,這樣在多次打開關閉后,因為數據庫緩存的問題,可能會引起程序的內存占用量激增超過20MB。於是用沒有一種折中的方案呢。我在網上找了很多ios上sqlite的開源庫,最終發現了很多程序都在用的FMDB(https://github.com/ccgus/fmdb)。這個庫對於多線程使用blocks的方式進行處理,可以在多個線程之間共享數據庫連接。使用這個庫就可以在整個程序運行期間只維護幾個數據庫連接。
 
     整個程序在多線程環境下的數據庫架構就清晰了起來。
     數據庫連接類WizDataBase,保持一個FMDB的數據庫連接,並提供針對程序的數據庫查詢方法。這個類實現WizDataBaseDeleage協議。WizDataBaseDeleage協議定義了程序中需要進行數據交互的所有數據庫操作。然后
/*
WizDataBaseDelegate.h
*/
@protocol WizDataBaseDelegate

// 具體的接口
@end

/*
WizDataBase.h
*/
@interface WizDataBase<WizDataBaseDelegate>
{
  FMDB* fmdb;
......
}
@end

 

     數據庫管理類(單例)WizDataBaseManager負責維護數據連接。實際上提供的是滿足WizDataBaseDeleage協議的對象。而不是一個具體的WizDataBase對象。這樣就可以通過類似與接口的方式,將數據庫層的具體實現屏蔽,只關心所需要的具體借口。
  
#import "WizDataBase.h"
#import "WizDataBaseDelegate.h"

@interface WizDbManager()
{
    NSMutableDictionary* dbDataDictionary;
}
@property (atomic, retain) NSMutableDictionary* dbDataDictionary;
- (void) clearDataBase;
- (id<WizDataBaseDelegate>) getWizDataBase;
@end

@implementation WizDbManager
@synthesize dbDataDictionary;
- (void) dealloc
{
    [dbDataDictionary release];
    [super dealloc];
}
...........

@end

 

      在具體使用的使用向WizDataBaseManager請求一個WizDataBase,然后使用。在這特意提一下一種常見的數據庫應用場景,就是在使用MVC的使用模型控制器從數據庫中讀取數據然后去更新UI。讀取數據庫當然是一個很費事的操作,如果讓主線程一直忙於讀取數據庫的話,將會降低UI的響應,讓界面看起來很卡。於是就很有必要將讀取數據的操作使用多線程的方式處理。apple提供很多多線程處理方式,NSOperation、GCD、Thread。在這種場景下,最適合的是GCD。寫個例子:
   
dispatch_async(dispatch_get_global_queue(0, 0), ^{
        if (nil == documentGuid) {
            return;
        }
        id<WizDataBaseDelegate> dataBase = [[WizDataBaseManager shareDbManager] getWizTempDataBase];
        WizAbstract* abstract = [dataBase abstractOfDocument:documentGuid];
        dispatch_async(dispatch_get_main_queue(), ^{
            if (nil == abstract) {
                return;
            }
            [self.data setObject:abstract forKey:documentGuid];
           imageView.image = abstract.image;
        });
    });

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM