Realm的簡單使用

前言

由于最近項目中在用Realm,所以把自己實踐過程中的一些心得總結分享一下。

Realm是由Y Combinator孵化的創業團隊開源出來的一款可以用于iOS(同樣適用于Swift&Objective-C)和Android的跨平臺移動數據庫。目前最新版是Realm 2.0.2,支持的平臺包括Java,Objective-C,Swift,React Native,Xamarin。

Realm官網上說了好多優點,我覺得選用Realm的最吸引人的優點就三點:

跨平臺:現在很多應用都是要兼顧iOS和Android兩個平臺同時開發。如果兩個平臺都能使用相同的數據庫,那就不用考慮內部數據的架構不同,使用Realm提供的API,可以使數據持久化層在兩個平臺上無差異化的轉換。

簡單易用:Core Data 和 SQLite 冗余、繁雜的知識和代碼足以嚇退絕大多數剛入門的開發者,而換用 Realm,則可以極大地減少學習成本,立即學會本地化存儲的方法。毫不吹噓的說,把官方最新文檔完整看一遍,就完全可以上手開發了。

可視化:Realm 還提供了一個輕量級的數據庫查看工具,在Mac Appstore 可以下載“Realm Browser”這個工具,開發者可以查看數據庫當中的內容,執行簡單的插入和刪除數據的操作。畢竟,很多時候,開發者使用數據庫的理由是因為要提供一些所謂的“知識庫”。

Realm Browser

“Realm Browser”這個工具調試起Realm數據庫實在太好用了,強烈推薦。

[RLMRealmConfiguration defaultConfiguration].fileURL

打印出Realm 數據庫地址,然后在Finder中??G跳轉到對應路徑下,用Realm Browser打開對應的.realm文件就可以看到數據啦.

如果是使用真機調試的話“Xcode->Window->Devices(??2)”,然后找到對應的設備與項目,點擊Download Container,導出xcappdata文件后,顯示包內容,進到AppData->Documents,使用Realm Browser打開.realm文件即可.

自2012年起, Realm 就已經開始被用于正式的商業產品中了。經過4年的使用,逐步趨于穩定。

Realm 安裝

使用 Realm 構建應用的基本要求:

  1. iOS 7 及其以上版本, macOS 10.9 及其以上版本,此外 Realm 支持 tvOS 和 watchOS 的所有版本。
  2. 需要使用 Xcode 7.3 或者以后的版本。

注意: 這里如果是純的OC項目,就安裝OC的Realm,如果是純的Swift項目,就安裝Swift的Realm。如果是混編項目,就需要安裝OC的Realm,然后要把 Swift/RLMSupport.swift 文件一同編譯進去。

RLMSupport.swift這個文件為 Objective-C 版本的 Realm 集合類型中引入了 Sequence 一致性,并且重新暴露了一些不能夠從 Swift 中進行原生訪問的 Objective-C 方法,例如可變參數 (variadic arguments)。更加詳細的說明見官方文檔

推薦的安裝方法:

  1. CocoaPods,在項目的Podfile中,添加pod 'Realm',在終端運行pod install。
  2. Static Framework
  • 下載 Realm 的最新版本并解壓,將 Realm.framework 從 ios/static/文件夾拖曳到您 Xcode 項目中的文件導航器當中。確保 Copy items if needed 選中然后單擊 Finish;
  • 在 Xcode 文件導航器中選擇您的項目,然后選擇您的應用目標,進入到 Build Phases 選項卡中。在 Link Binary with Libraries 中單擊 + 號然后添加libc++.dylib;

Realm 中的相關術語

為了能更好的理解Realm的使用,先介紹一下涉及到的相關術語。

RLMRealm:Realm是框架的核心所在,是我們構建數據庫的訪問點,就如同Core Data的管理對象上下文(managed object context)一樣。出于簡單起見,realm提供了一個默認的defaultRealm( )的便利構造器方法。

RLMObject:這是我們自定義的Realm數據模型。創建數據模型的行為對應的就是數據庫的結構。要創建一個數據模型,我們只需要繼承RLMObject,然后設計我們想要存儲的屬性即可。

關系(Relationships):通過簡單地在數據模型中聲明一個RLMObject類型的屬性,我們就可以創建一個“一對多”的對象關系。同樣地,我們還可以創建“多對一”和“多對多”的關系。

寫操作事務(Write Transactions):數據庫中的所有操作,比如創建、編輯,或者刪除對象,都必須在事務中完成。“事務”是指位于write閉包內的代碼段。

查詢(Queries):要在數據庫中檢索信息,我們需要用到“檢索”操作。檢索最簡單的形式是對Realm( )數據庫發送查詢消息。如果需要檢索更復雜的數據,那么還可以使用斷言(predicates)、復合查詢以及結果排序等等操作。

RLMResults:這個類是執行任何查詢請求后所返回的類,其中包含了一系列的RLMObject對象。RLMResults和NSArray類似,我們可以用下標語法來對其進行訪問,并且還可以決定它們之間的關系。不僅如此,它還擁有許多更強大的功能,包括排序、查找等等操作。

Realm如何使用

由于Realm的API極為友好,一看就懂,所以這里就按照平時開發的順序,把需要用到的都梳理一遍。

  1. 創建數據庫
  • 一般地,我們使用的為默認的Realm數據庫,即調用[RLMRealm defaultRealm]來初始化以及訪問我們的realm變量。這個方法將會返回一個 RLMRealm對象,并指向您應用的 Documents (iOS) 文件夾下的一個名為“default.realm”的文件。

  • 用自己創建的數據庫

-(void)creatDataBaseWithName:(NSString *)databaseName {
    NSArray *docPath = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
    NSString *path = [docPath objectAtIndex:0];
    NSString *filePath = [path stringByAppendingPathComponent:databaseName];
    NSLog(@"數據庫目錄 = %@",filePath);
    RLMRealmConfiguration *config = [RLMRealmConfiguration defaultConfiguration];
    config.fileURL = [NSURL URLWithString:filePath];
    config.objectClasses = @[MyClass.class, MyOtherClass.class];
    config.readOnly = NO;
    int currentVersion = 1.0;
    config.schemaVersion = currentVersion;
    config.migrationBlock = ^(RLMMigration *migration , uint64_t oldSchemaVersion) {
       // 這里是設置數據遷移的block
        if (oldSchemaVersion < currentVersion) {
        }
    };
    [RLMRealmConfiguration setDefaultConfiguration:config];
}

拓展:

// 查詢指定的 Realm 數據庫
RLMRealm *petsRealm = [RLMRealm realmWithPath:@"pets.realm"]; // 獲得一個指定的 Realm 數據庫
RLMResults *otherDogs = [Dog allObjectsInRealm:petsRealm]; // 從該 Realm 數據庫中,檢索所有狗狗

創建數據庫主要設置RLMRealmConfiguration,設置數據庫名字和存儲地方。把路徑以及數據庫名字拼接好字符串,賦值給fileURL即可。

objectClasses這個屬性是用來控制對哪個類能夠存儲在指定 Realm 數據庫中做出限制。例如,如果有兩個團隊分別負責開發您應用中的不同部分,并且同時在應用內部使用了 Realm 數據庫,那么您肯定不希望為它們協調進行數據遷移您可以通過設置RLMRealmConfiguration的 objectClasses屬性來對類做出限制。objectClasses一般可以不用設置。
readOnly是控制是否只讀屬性。

2.建表

  • 創建簡單數據模型
#import <Realm/Realm.h>

@interface MJCoutryModel : RLMObject

@property (nonatomic, copy) NSString *countryId;

@property (nonatomic, copy) NSString *country;

@property (nonatomic, copy) NSString *dialCode;

@end

RLM_ARRAY_TYPE(MJCoutryModel)

可以設置配置

//主鍵
+ (NSString *)primaryKey {
    return @"countryId";
}

////設置屬性默認值
//+ (NSDictionary *)defaultPropertyValues {
//    return @{@"dialCode":@"00" };
//}

//設置忽略屬性,即不存到realm數據庫中
+ (NSArray<NSString *> *)ignoredProperties {
    return @[@"country"];
}

//一般來說,屬性為nil的話realm會拋出異常,但是如果實現了這個方法的話,就只有countryId為nil會拋出異常,也就是說現在dialCode屬性可以為空了
+ (NSArray *)requiredProperties {
    return @[@"countryId"];
}

//設置索引,可以加快檢索的速度
+ (NSArray *)indexedProperties {
    return @[@"countryId"];
}
  • 創建嵌套數據模型
#import <Realm/Realm.h>
@class Person;
// 狗狗的數據模型
@interface Dog : RLMObject
@property NSString *name;
@property Person   *owner;
@end
RLM_ARRAY_TYPE(Dog) // 定義RLMArray<Dog>
// 狗狗主人的數據模型
@interface Person : RLMObject
@property NSString      *name;
@property NSDate        *birthdate;
// 通過RLMArray建立關系
@property RLMArray<Dog> *dogs;
@end
RLM_ARRAY_TYPE(Person) // 定義RLMArray<Person>

注意:RLMObject 官方建議不要加上 Objective-C的property attributes(如nonatomic, atomic, strong, copy, weak 等等)假如設置了,這些attributes會一直生效直到RLMObject被寫入realm數據庫。

3.增

    Dog *myDog = [[Dog alloc] init];
    myDog.name = @"小花";
    
    Dog *yourDog = [[Dog alloc] init];
    yourDog.name = @"小黑";
    
    Person *me = [[Person alloc] initWithValue:@[@"小明",[NSDate dateWithTimeIntervalSinceNow:1],@[myDog,yourDog]]];
    
    yourDog.owner = me;
    myDog.owner = me;
    
    
    RLMRealm *realm = [RLMRealm defaultRealm];
    [realm beginWriteTransaction];
    [realm addObject:yourDog];
    [realm addObject:myDog];
    [realm addObject:me];
    [realm commitWriteTransaction];

使用Realm進行數據管理的方式:
方式一:

 RLMRealm *realm = [RLMRealm defaultRealm];

// 開放RLMRealm事務
[realm beginWriteTransaction];

// 在開放開放/提交事務之間進行數據處理

// 提交事務
[realm commitWriteTransaction];

方式二:

[realm transactionWithBlock:^{

 // 進行數據處理

}];

4.刪

清空所有數據

   
    RLMRealm *realm = [RLMRealm defaultRealm];
    RLMResults<Person *> *personResults = [Person allObjects];
    if (personResults.count > 0) {
        [realm beginWriteTransaction];
        [realm deleteObjects:personResults];
        [realm commitWriteTransaction];
    }
    
    RLMResults<Dog *> *dogResults = [Dog allObjects];
        [realm beginWriteTransaction];
        [realm deleteAllObjects];
        [realm commitWriteTransaction];

清空某條數據

   
    RLMRealm *realm = [RLMRealm defaultRealm];
    RLMResults<MJCoutryModel *> *walletResults = [MJCoutryModel allObjects];
    MJCoutryModel *wallet1 = [walletResults firstObject];

    [realm beginWriteTransaction];
    [realm deleteObject:wallet1];
    [realm commitWriteTransaction];

5.查
數據庫查詢

// 查詢默認的 Realm 數據庫
RLMResults *dogs = [Dog allObjects]; // 從默認的 Realm 數據庫中,檢索所有狗狗

如果有需要,也可以查詢指定的數據庫

// 查詢指定的 Realm 數據庫
RLMRealm *petsRealm = [RLMRealm realmWithPath:@"pets.realm"]; // 獲得一個指定的 Realm 數據庫
RLMResults *otherDogs = [Dog allObjectsInRealm:petsRealm]; // 從該 Realm 數據庫中,檢索所有狗狗

條件查詢
1.使用斷言字符串查詢:

RLMResults *tanDogs = [Dog objectsWhere:@"color = '棕黃色' AND name BEGINSWITH '大'" ascending:YES];

2.使用 NSPredicate 查詢

NSPredicate *pred = [NSPredicate predicateWithFormat:@"color = %@ AND name BEGINSWITH %@",
                                                 @"棕黃色", @"大"];
RLMResults *tanDogs = [Dog objectsWithPredicate:pred];

3.鏈式查詢
如果我們想獲得獲得棕黃色狗狗的查詢結果,并且在這個查詢結果的基礎上再獲得名字以“大”開頭的棕黃色狗狗。

RLMResults *tanDogs = [Dog objectsWhere:@"color = '棕黃色'"];
RLMResults *tanDogsWithBNames = [tanDogs objectsWhere:@"name BEGINSWITH '大'"];

6.改

  • 當沒有主鍵的情況下,需要先查詢,再修改數據。
   // 先查詢
    RLMRealm *realm = [RLMRealm defaultRealm];
    NSPredicate *pred = [NSPredicate predicateWithFormat:@"dialCode = %@ AND country BEGINSWITH %@",@"123", @"中國"];
    RLMResults<MJCoutryModel *> *walletResults = [MJCoutryModel objectsWithPredicate:pred];
   // 再修改
    for (int i = 0; i < walletResults.count; i++) {
        MJCoutryModel *wallet = walletResults[i];
        [realm beginWriteTransaction];
        wallet.country = @"托馬斯品欽";
        [realm commitWriteTransaction];
    }
  • 當有主鍵的情況下,有以下幾個非常好用的API
    RLMRealm *realm = [RLMRealm defaultRealm];
    
    MJCoutryModel *country = [[MJCoutryModel alloc] init];
    country.countryId = @"21";
    country.country = @"ee21";
    country.dialCode = @"22";
    
    [realm beginWriteTransaction];
    // 方法一
    [MJCoutryModel createOrUpdateInRealm:realm withValue:country];
    // 方法二
    [realm addOrUpdateObject:country];
    
    [realm commitWriteTransaction];

addOrUpdateObject: 會去先查找有沒有傳入的Car相同的主鍵,如果有,就更新該條數據。這里需要注意,addOrUpdateObject這個方法不是增量更新,所有的值都必須有,如果有哪幾個值是null,那么就會覆蓋原來已經有的值,這樣就會出現數據丟失的問題。

createOrUpdateInRealm:withValue:這個方法是增量更新的,后面傳一個字典,使用這個方法的前提是有主鍵。方法會先去主鍵里面找有沒有字典里面傳入的主鍵的記錄,如果有,就只更新字典里面的子集。如果沒有,就新建一條記錄。

數據遷移

當您使用任意一個數據庫時,您隨時都可能打算修改您的數據模型。通過設置 RLMRealmConfiguration.schemaVersion 以及RLMRealmConfiguration.migrationBlock 可以定義一個遷移操作以及與之關聯的架構版本。 遷移閉包將會提供提供相應的邏輯操作,以讓數據模型從之前的架構轉換到新的架構中來。 每當通過配置創建完一個 RLMRealm 之后,遷移閉包將會在遷移需要的時候,將給定的架構版本應用到更新 RLMRealm 操作中。
如下所示是最簡單的數據遷移的必需流程:

// 在 [AppDelegate didFinishLaunchingWithOptions:] 中進行配置

RLMRealmConfiguration *config = [RLMRealmConfiguration defaultConfiguration];
config.schemaVersion = 2;
config.migrationBlock = ^(RLMMigration *migration, uint64_t oldSchemaVersion)
{
    // enumerateObjects:block: 遍歷了存儲在 Realm 文件中的每一個“Person”對象
    [migration enumerateObjects:MJCoutryModel.className block:^(RLMObject *oldObject, RLMObject *newObject) {
        // 只有當 Realm 數據庫的架構版本為 0 的時候,才添加 “fullName” 屬性
        if (oldSchemaVersion < 1) {
            newObject[@"fullName"] = [NSString stringWithFormat:@"%@ %@", oldObject[@"firstName"], oldObject[@"lastName"]];
        }
        // 只有當 Realm 數據庫的架構版本為 0 或者 1 的時候,才添加“email”屬性
        if (oldSchemaVersion < 2) {
            newObject[@"email"] = @"";
        }
       // 替換屬性名
       if (oldSchemaVersion < 3) { // 重命名操作應該在調用 `enumerateObjects:` 之外完成 
            [migration renamePropertyForClass:Person.className oldName:@"yearsSinceBirth" newName:@"age"]; }
    }];
};
[RLMRealmConfiguration setDefaultConfiguration:config];
// 現在我們已經成功更新了架構版本并且提供了遷移閉包,打開舊有的 Realm 數據庫會自動執行此數據遷移,然后成功進行訪問
[RLMRealm defaultRealm];

參考鏈接

Realm官網
Realm官方文檔
Realm GitHub
參考博客一
參考博客二

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 229,001評論 6 537
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,786評論 3 423
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,986評論 0 381
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,204評論 1 315
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,964評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,354評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,410評論 3 444
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,554評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,106評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,918評論 3 356
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,093評論 1 371
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,648評論 5 362
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,342評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,755評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,009評論 1 289
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,839評論 3 395
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,107評論 2 375

推薦閱讀更多精彩內容