iOS Realm數(shù)據(jù)庫<轉(zhuǎn)>

去年在個人的兩個小項(xiàng)目中嘗試著用了realm,當(dāng)時感覺簡直就時我的最愛馆铁,尤其是像我這種本身對sql不是特別熟悉的人來說榨为,以前在公司的項(xiàng)目中使用數(shù)據(jù)庫的時候,覺得太變態(tài)了啃勉,當(dāng)時公司項(xiàng)目設(shè)計了一個專門用來管理數(shù)據(jù)庫的類忽舟,一開始不是用的fmdb,而是直接使用原生的sql3的動態(tài)庫淮阐,項(xiàng)目做完后叮阅,這個數(shù)據(jù)庫管理類里面寫滿了sql的代碼,有的時候?qū)?shù)據(jù)庫做些小的更改整個人都感覺不好了

前言

Realm是由Y Combinator孵化的創(chuàng)業(yè)團(tuán)隊(duì)開源出來的一款可以用于iOS(同樣適用于Swift&Objective-C)和Android的跨平臺移動數(shù)據(jù)庫泣特。目前最新版是Realm 3.7.4浩姥,支持的平臺包括Java,Objective-C状您,Swift勒叠,React Native,Xamarin膏孟。

環(huán)境要求

  • Xcode 8.3.3 or higher
  • Target of iOS 8 or higher, macOS 10.9 or higher, or any version of tvOS or watchOS

安裝

Dynamic Framework

  1. 下載最新的最新的Realm發(fā)行版本眯分,并解壓

  2. 前往Xcode 工程的”General”設(shè)置項(xiàng)中,從ios/dynamic/, osx/, tvos/ 或者 watchos/中將Realm.framework拖曳到”Embedded Binaries”選項(xiàng)中柒桑。確認(rèn)Copy items if needed被選中后弊决,點(diǎn)擊Finish按鈕

  3. 在單元測試 Target 的”Build Settings”中,在”Framework Search Paths”中添加Realm.framework的上級目錄幕垦;

  4. 如果希望使用 Swift 加載 Realm丢氢,請拖動Swift/RLMSupport.swift文件到 Xcode 工程的文件導(dǎo)航欄中并選中Copy items if needed

  5. 如果在 iOS先改、watchOS 或者 tvOS 項(xiàng)目中使用 Realm疚察,請?jiān)谀鷳?yīng)用目標(biāo)的”Build Phases”中,創(chuàng)建一個新的”Run Script Phase”仇奶,并將bash "${BUILT_PRODUCTS_DIR}/${FRAMEWORKS_FOLDER_PATH}/Realm.framework/strip-frameworks.sh"這條腳本復(fù)制到文本框中貌嫡。 因?yàn)橐@過APP商店提交的bug比驻,這一步在打包通用設(shè)備的二進(jìn)制發(fā)布版本時是必須的。

CocoaPods
在項(xiàng)目的Podfile中岛抄,添加pod 'Realm'别惦,在終端運(yùn)行pod install

Realm 中的相關(guān)術(shù)語

  • RLMRealm:Realm是框架的核心所在,是我們構(gòu)建數(shù)據(jù)庫的訪問點(diǎn)夫椭,就如同Core Data的管理對象上下文(managed object context)一樣掸掸。出于簡單起見,realm提供了一個默認(rèn)的defaultRealm( )的便利構(gòu)造器方法蹭秋。

  • RLMObject:這是我們自定義的Realm數(shù)據(jù)模型扰付。創(chuàng)建數(shù)據(jù)模型的行為對應(yīng)的就是數(shù)據(jù)庫的結(jié)構(gòu)。要創(chuàng)建一個數(shù)據(jù)模型仁讨,我們只需要繼承RLMObject羽莺,然后設(shè)計我們想要存儲的屬性即可。

  • 關(guān)系(Relationships):通過簡單地在數(shù)據(jù)模型中聲明一個RLMObject類型的屬性洞豁,我們就可以創(chuàng)建一個“一對多”的對象關(guān)系盐固。同樣地,我們還可以創(chuàng)建“多對一”和“多對多”的關(guān)系丈挟。

  • 寫操作事務(wù)(Write Transactions):數(shù)據(jù)庫中的所有操作刁卜,比如創(chuàng)建、編輯曙咽,或者刪除對象长酗,都必須在事務(wù)中完成⊥┤蓿“事務(wù)”是指位于write閉包內(nèi)的代碼段。

  • 查詢(Queries):要在數(shù)據(jù)庫中檢索信息之拨,我們需要用到“檢索”操作茉继。檢索最簡單的形式是對Realm( )數(shù)據(jù)庫發(fā)送查詢消息。如果需要檢索更復(fù)雜的數(shù)據(jù)蚀乔,那么還可以使用斷言(predicates)烁竭、復(fù)合查詢以及結(jié)果排序等等操作。

  • RLMResults:這個類是執(zhí)行任何查詢請求后所返回的類吉挣,其中包含了一系列的RLMObject對象派撕。RLMResults和NSArray類似,我們可以用下標(biāo)語法來對其進(jìn)行訪問睬魂,并且還可以決定它們之間的關(guān)系终吼。不僅如此,它還擁有許多更強(qiáng)大的功能氯哮,包括排序际跪、查找等等操作。

開始

1.創(chuàng)建數(shù)據(jù)庫
- (void)creatDataBaseWithName:(NSString *)databaseName
{
    NSArray *docPath = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
    NSString *path = [docPath objectAtIndex:0];
    NSString *filePath = [path stringByAppendingPathComponent:databaseName];
    
    RLMRealmConfiguration *config = [RLMRealmConfiguration defaultConfiguration];
    config.fileURL = [NSURL URLWithString:filePath];
    config.readOnly = NO;
    int currentVersion = 1.0;
    config.schemaVersion = currentVersion;
    config.migrationBlock = ^(RLMMigration *migration , uint64_t oldSchemaVersion) {
        if (oldSchemaVersion < currentVersion) {
            // 這里是設(shè)置數(shù)據(jù)遷移的block
        }
    };
    [RLMRealmConfiguration setDefaultConfiguration:config];
}
2.建表

Realm數(shù)據(jù)模型是基于標(biāo)準(zhǔn) Objective?C 類來進(jìn)行定義的,使用屬性來完成模型的具體定義姆打。
我們只需要繼承 RLMObject或者一個已經(jīng)存在的模型類良姆,您就可以創(chuàng)建一個新的 Realm 數(shù)據(jù)模型對象。對應(yīng)在數(shù)據(jù)庫里面就是一張表幔戏。

#import <Realm/Realm.h>
#import <Foundation/Foundation.h>

@interface Test : RLMObject

@end
RLM_ARRAY_TYPE(Test)

@interface HTMainAccountsSubModel : RLMObject
@property  NSString * subTitle;//輔助密碼名稱  *******  必須不為空
@property  NSString * password;//輔助密碼
@property (readonly) RLMLinkingObjects *owners;
@property  Test * test;
@end
RLM_ARRAY_TYPE(HTMainAccountsSubModel)

@interface HTMainAccountsModel : RLMObject
@property  NSString * k_push_id;    //分類跳轉(zhuǎn)使用的id    默認(rèn)為@"acount"
@property  NSString * k_id;         //分類id   *******   必須不為空 默認(rèn)為 @"0"
@property  NSString * a_id;         //賬號id   *******   必須不為空
@property  NSString * accountTitle; //標(biāo)題  *******      必須不為空

@property  NSString * creatTime;    //創(chuàng)建時間
@property  NSString * changeTime;   //修改時間

@property  NSString * account;      //賬號            可以為空
@property  NSString * password;     //密碼            可以為空
@property  NSString * remarks;      //備注            可以為空
@property  BOOL       isCollect;    //是否被收藏
@property  NSInteger  iconType;     //icon類型,實(shí)際上只是為了取圖標(biāo)使用  默認(rèn)為0;

@property  RLMArray<HTMainAccountsSubModel> *infoPassWord;//輔助密碼信息 例如:交易密碼,其它非登錄密碼信息
@end
RLM_ARRAY_TYPE(HTMainAccountsModel)
@implementation HTMainAccountsSubModel
//一般來說,屬性為nil的話realm會拋出異常,但是如果實(shí)現(xiàn)了這個方法的話,就只有subTitle為nil會拋出異常,也就是說現(xiàn)在其他屬性可以為空了
+ (NSArray *)requiredProperties {
    return @[@"subTitle"];
}
//設(shè)置屬性默認(rèn)值
+ (NSDictionary *)defaultPropertyValues{
    return @{@"subTitle":@"密碼"
             };
}
//鏈接反向關(guān)系
+ (NSDictionary *)linkingObjectsProperties {
    return @{
             @"owners": [RLMPropertyDescriptor descriptorWithClass:HTMainAccountsModel.class propertyName:@"infoPassWord"],
             };
}
@end

@implementation HTMainAccountsModel
//一般來說,屬性為nil的話realm會拋出異常,但是如果實(shí)現(xiàn)了這個方法的話,就只有accountTitle等等為nil會拋出異常,也就是說現(xiàn)在其他屬性可以為空了
+ (NSArray *)requiredProperties {
    return @[@"accountTitle",
             @"k_push_id",
             @"a_id",
             @"k_id"];
}
// 主鍵
+ (NSString *)primaryKey {
    return @"a_id";
}
//設(shè)置屬性默認(rèn)值
+ (NSDictionary *)defaultPropertyValues{
    return @{@"isCollect":@(NO),
             @"iconType":@(0),
             @"k_push_id":@"acount",
             @"k_id":@"0"
             };
}
@end

還可以給RLMObject設(shè)置主鍵primaryKey玛追,默認(rèn)值defaultPropertyValues,忽略的屬性ignoredProperties闲延,必要屬性requiredProperties痊剖,索引indexedProperties。比較有用的是主鍵和索引

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

RLM_ARRAY_TYPE宏創(chuàng)建了一個協(xié)議侍匙,從而允許RLMArray<HTMainAccountsSubModel>語法的使用氮惯。如果該宏沒有放置在模型接口的底部的話,您或許需要提前聲明該模型類想暗。

  • 關(guān)于RLMObject的的關(guān)系

    1. 對一(To-One)關(guān)系
    對于多對一(many-to-one)或者一對一(one-to-one)關(guān)系來說妇汗,只需要聲明一個RLMObject子類類型的屬性即可,如上面代碼例子说莫,@property  Test * test; 
    
    1. 對多(To-Many)關(guān)系
    通過 RLMArray類型的屬性您可以定義一個對多關(guān)系杨箭。如上面代碼例子,@property  RLMArray<HTMainAccountsSubModel> *infoPassWord;
    
    1. 反向關(guān)系(Inverse Relationship)
    鏈接是單向性的储狭。
    因此互婿,如果對多關(guān)系屬性 HTMainAccountsModel.infoPassWord鏈接了一個 HTMainAccountsSubModel實(shí)例,
    而這個實(shí)例的對一關(guān)系屬性 HTMainAccountsSubModel.owner又鏈接到了對應(yīng)的這個 HTMainAccountsModel實(shí)例辽狈,
    那么實(shí)際上這些鏈接仍然是互相獨(dú)立的慈参。
    
    //反向關(guān)系
    + (NSDictionary *)linkingObjectsProperties {
    return @{
             @"owners": [RLMPropertyDescriptor descriptorWithClass:HTMainAccountsModel.class propertyName:@"infoPassWord"],
             };
    

}
```

3.創(chuàng)建對象
// (1) 創(chuàng)建一個HTMainAccountsModel對象,然后設(shè)置其屬性
HTMainAccountsModel *model = [[HTMainAccountsModel alloc] init];
model.k_push_id = @"";
model.k_id = @"";
model.a_id = @"";
model.creatTime = @"";


// (2) 通過字典創(chuàng)建HTMainAccountsModel對象
HTMainAccountsModel *model = [[HTMainAccountsModel alloc]initWithValue:@{@"k_push_id":self.item.k_push_id,@"k_id":self.item.k_id,"a_id":@(creatTime).stringValue,@"creatTime":@(creatTime).stringValue}];

// (3) 通過數(shù)組創(chuàng)建HTMainAccountsModel對象
HTMainAccountsModel *model = [[HTMainAccountsModel alloc] initWithValue:@[]];//這行完全就只是示例

4.增
//addObject
RLMRealm *realm = [RLMRealm defaultRealm];
[realm beginWriteTransaction];
[realm addObject:model];
[realm commitWriteTransaction];

//addOrUpdateObject 當(dāng)realm中已經(jīng)存在這個對象則調(diào)用這個方法
//addOrUpdateObject會去先查找有沒有傳入的model相同的主鍵刮萌,如果有驮配,就更新該條數(shù)據(jù)。這里需要注意着茸,addOrUpdateObject這個方法不是增量更新壮锻,所有的值都必須有,如果有哪幾個值是null涮阔,那么就會覆蓋原來已經(jīng)有的值猜绣,這樣就會出現(xiàn)數(shù)據(jù)丟失的問題。
RLMRealm *realm = [RLMRealm defaultRealm];
[realm beginWriteTransaction];
[realm addOrUpdateObject:model];
[realm commitWriteTransaction];
5.刪
[realm beginWriteTransaction];
// 刪除單條記錄
[realm deleteObject:model];
// 刪除多條記錄
[realm deleteObjects:modelResult];
// 刪除所有記錄
[realm deleteAllObjects];

[realm commitWriteTransaction];
6.改
[realm addOrUpdateObject:model];

[HTMainAccountsModel createOrUpdateInRealm:realm withValue:@{@"k_id": @1}];
7.查

在Realm中所有的查詢(包括查詢和屬性訪問)在 Realm 中都是延遲加載的敬特,只有當(dāng)屬性被訪問時途事,才能夠讀取相應(yīng)的數(shù)據(jù)验懊。

查詢結(jié)果并不是數(shù)據(jù)的拷貝:修改查詢結(jié)果(在寫入事務(wù)中)會直接修改硬盤上的數(shù)據(jù)。同樣地尸变,您可以直接通過包含在RLMResults中的RLMObject對象完成遍歷關(guān)系圖的操作义图。除非查詢結(jié)果被使用,否則檢索的執(zhí)行將會被推遲召烂。這意味著鏈接幾個不同的臨時 {RLMResults} 來進(jìn)行排序和匹配數(shù)據(jù)碱工,不會執(zhí)行額外的工作,例如處理中間狀態(tài)奏夫。

一旦檢索執(zhí)行之后怕篷,或者通知模塊被添加之后, RLMResults將隨時保持更新酗昼,接收 Realm 中廊谓,在后臺線程上執(zhí)行的檢索操作中可能所做的更改。

//從默認(rèn)數(shù)據(jù)庫查詢所有的車
RLMResults<HTMainAccountsModel *> *model = [HTMainAccountsModel allObjects];

// 使用斷言字符串查詢
RLMResults<HTMainAccountsModel *> *model = [HTMainAccountsModel objectsWhere:@"kid = '' AND accountTitle BEGINSWITH 'm'"];

// 使用 NSPredicate 查詢
NSPredicate *pred = [NSPredicate predicateWithFormat:@"kid = %@ AND accountTitle BEGINSWITH %@",@"1", @"mm"];
RLMResults *results = [HTMainAccountsModel objectsWithPredicate:pred];

// 排序
RLMResults<HTMainAccountsModel *> *model = [[HTMainAccountsModel objectsWhere:@"kid = '11' AND accountTitle BEGINSWITH 'm'"] sortedResultsUsingProperty:@"a_id" ascending:YES];

- 跨線程訪問數(shù)據(jù)庫麻削,Realm對象一定需要新建一個,自己封裝一個Realm全局實(shí)例單例是沒啥作用的

很多開發(fā)者應(yīng)該都會對Core Data和Sqlite3或者FMDB蒸痹,自己封裝一個類似Helper的單例。于是我也在這里封裝了一個單例呛哟,在新建完Realm數(shù)據(jù)庫的時候strong持有一個Realm的對象叠荠。然后之后的訪問中只需要讀取這個單例持有的Realm對象就可以拿到數(shù)據(jù)庫了。

想法是好的扫责,但是同一個Realm對象是不支持跨線程操作realm數(shù)據(jù)庫的榛鼎。

Realm 通過確保每個線程始終擁有 Realm 的一個快照,以便讓并發(fā)運(yùn)行變得十分輕松鳖孤。你可以同時有任意數(shù)目的線程訪問同一個 Realm 文件者娱,并且由于每個線程都有對應(yīng)的快照,因此線程之間絕不會產(chǎn)生影響苏揣。需要注意的一件事情就是不能讓多個線程都持有同一個 Realm 對象的 實(shí)例 肺然。如果多個線程需要訪問同一個對象,那么它們分別會獲取自己所需要的實(shí)例(否則在一個線程上發(fā)生的更改就會造成其他線程得到不完整或者不一致的數(shù)據(jù))腿准。

其實(shí)RLMRealm *realm = [RLMRealm defaultRealm]; 這句話就是獲取了當(dāng)前realm對象的一個實(shí)例,其實(shí)實(shí)現(xiàn)就是拿到單例拾碌。所以我們每次在子線程里面不要再去讀取我們自己封裝持有的realm實(shí)例了吐葱,直接調(diào)用系統(tǒng)的這個方法即可,能保證訪問不出錯校翔。

transactionWithBlock 已經(jīng)處于一個寫的事務(wù)中弟跑,事務(wù)之間不能嵌套

[realm transactionWithBlock:^{
                [self.realm beginWriteTransaction];
                [self convertToRLMUserWith:bhUser To:[self convertToRLMUserWith:bhUser To:nil]];
                [self.realm commitWriteTransaction];
            }];

建議每個model都需要設(shè)置主鍵,這樣可以方便add和update

如果能設(shè)置主鍵防症,請盡量設(shè)置主鍵孟辑,
因?yàn)檫@樣方便我們更新數(shù)據(jù)哎甲,
我們可以很方便的調(diào)用addOrUpdateObject: 或者 createOrUpdateInRealm:withValue:方法進(jìn)行更新。
這樣就不需要先根據(jù)主鍵饲嗽,查詢出數(shù)據(jù)炭玫,
然后再去更新。
有了主鍵以后貌虾,這兩步操作可以一步完成吞加。
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市尽狠,隨后出現(xiàn)的幾起案子衔憨,更是在濱河造成了極大的恐慌,老刑警劉巖袄膏,帶你破解...
    沈念sama閱讀 211,265評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件践图,死亡現(xiàn)場離奇詭異,居然都是意外死亡沉馆,警方通過查閱死者的電腦和手機(jī)码党,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,078評論 2 385
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來悍及,“玉大人闽瓢,你說我怎么就攤上這事⌒母希” “怎么了扣讼?”我有些...
    開封第一講書人閱讀 156,852評論 0 347
  • 文/不壞的土叔 我叫張陵,是天一觀的道長缨叫。 經(jīng)常有香客問我椭符,道長,這世上最難降的妖魔是什么耻姥? 我笑而不...
    開封第一講書人閱讀 56,408評論 1 283
  • 正文 為了忘掉前任销钝,我火速辦了婚禮,結(jié)果婚禮上琐簇,老公的妹妹穿的比我還像新娘蒸健。我一直安慰自己,他們只是感情好婉商,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,445評論 5 384
  • 文/花漫 我一把揭開白布似忧。 她就那樣靜靜地躺著,像睡著了一般丈秩。 火紅的嫁衣襯著肌膚如雪盯捌。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,772評論 1 290
  • 那天蘑秽,我揣著相機(jī)與錄音饺著,去河邊找鬼箫攀。 笑死,一個胖子當(dāng)著我的面吹牛幼衰,可吹牛的內(nèi)容都是我干的靴跛。 我是一名探鬼主播,決...
    沈念sama閱讀 38,921評論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼塑顺,長吁一口氣:“原來是場噩夢啊……” “哼汤求!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起严拒,我...
    開封第一講書人閱讀 37,688評論 0 266
  • 序言:老撾萬榮一對情侶失蹤扬绪,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后裤唠,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體挤牛,經(jīng)...
    沈念sama閱讀 44,130評論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,467評論 2 325
  • 正文 我和宋清朗相戀三年种蘸,在試婚紗的時候發(fā)現(xiàn)自己被綠了墓赴。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,617評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡航瞭,死狀恐怖诫硕,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情刊侯,我是刑警寧澤章办,帶...
    沈念sama閱讀 34,276評論 4 329
  • 正文 年R本政府宣布,位于F島的核電站滨彻,受9級特大地震影響藕届,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜亭饵,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,882評論 3 312
  • 文/蒙蒙 一休偶、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧辜羊,春花似錦踏兜、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,740評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至喜德,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間垮媒,已是汗流浹背舍悯。 一陣腳步聲響...
    開封第一講書人閱讀 31,967評論 1 265
  • 我被黑心中介騙來泰國打工航棱, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人萌衬。 一個月前我還...
    沈念sama閱讀 46,315評論 2 360
  • 正文 我出身青樓饮醇,卻偏偏與公主長得像,于是被迫代替她去往敵國和親秕豫。 傳聞我的和親對象是個殘疾皇子朴艰,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,486評論 2 348

推薦閱讀更多精彩內(nèi)容