跨平臺(tái):現(xiàn)在很多應(yīng)用都是要兼顧iOS和Android兩個(gè)平臺(tái)同時(shí)開(kāi)發(fā)状婶。如果兩個(gè)平臺(tái)都能使用相同的數(shù)據(jù)庫(kù),那就不用考慮內(nèi)部數(shù)據(jù)的架構(gòu)不同馅巷,使用Realm提供的API膛虫,可以使數(shù)據(jù)持久化層在兩個(gè)平臺(tái)上無(wú)差異化的轉(zhuǎn)換。
簡(jiǎn)單易用:Core Data 和 SQLite 冗余钓猬、繁雜的知識(shí)和代碼足以嚇退絕大多數(shù)剛?cè)腴T(mén)的開(kāi)發(fā)者稍刀,而換用 Realm,則可以極大地減少學(xué)習(xí)成本,立即學(xué)會(huì)本地化存儲(chǔ)的方法账月。毫不吹噓的說(shuō)综膀,把官方最新文檔完整看一遍,就完全可以上手開(kāi)發(fā)了局齿。
可視化:Realm 還提供了一個(gè)輕量級(jí)的數(shù)據(jù)庫(kù)查看工具剧劝,在Mac Appstore 可以下載“Realm Browser”這個(gè)工具,開(kāi)發(fā)者可以查看數(shù)據(jù)庫(kù)當(dāng)中的內(nèi)容抓歼,執(zhí)行簡(jiǎn)單的插入和刪除數(shù)據(jù)的操作讥此。畢竟,很多時(shí)候锭部,開(kāi)發(fā)者使用數(shù)據(jù)庫(kù)的理由是因?yàn)橐峁┮恍┧^的“知識(shí)庫(kù)”暂论。
“Realm Browser”這個(gè)工具調(diào)試起Realm數(shù)據(jù)庫(kù)實(shí)在太好用了,強(qiáng)烈推薦拌禾。
如果使用模擬器進(jìn)行調(diào)試,可以通過(guò)
[RLMRealmConfiguration defaultConfiguration].fileURL
打印出Realm 數(shù)據(jù)庫(kù)地址,然后在Finder中??G跳轉(zhuǎn)到對(duì)應(yīng)路徑下,用Realm Browser打開(kāi)對(duì)應(yīng)的.realm文件就可以看到數(shù)據(jù)啦.
如果是使用真機(jī)調(diào)試的話(huà)“Xcode->Window->Devices(??2)”,然后找到對(duì)應(yīng)的設(shè)備與項(xiàng)目,點(diǎn)擊Download Container取胎,導(dǎo)出xcappdata文件后,顯示包內(nèi)容,進(jìn)到AppData->Documents,使用Realm Browser打開(kāi).realm文件即可.
自2012年起, Realm 就已經(jīng)開(kāi)始被用于正式的商業(yè)產(chǎn)品中了湃窍。經(jīng)過(guò)4年的使用闻蛀,逐步趨于穩(wěn)定。
一. Realm 安裝
使用 Realm 構(gòu)建應(yīng)用的基本要求:
iOS 7 及其以上版本, macOS 10.9 及其以上版本您市,此外 Realm 支持 tvOS 和 watchOS 的所有版本觉痛。
需要使用 Xcode 7.3 或者以后的版本。
注意 這里如果是純的OC項(xiàng)目茵休,就安裝OC的Realm薪棒,如果是純的Swift項(xiàng)目,就安裝Swift的Realm榕莺。如果是混編項(xiàng)目俐芯,就需要安裝OC的Realm,然后要把 Swift/RLMSupport.swift 文件一同編譯進(jìn)去钉鸯。
RLMSupport.swift這個(gè)文件為 Objective-C 版本的 Realm 集合類(lèi)型中引入了 Sequence 一致性吧史,并且重新暴露了一些不能夠從 Swift 中進(jìn)行原生訪問(wèn)的 Objective-C 方法,例如可變參數(shù) (variadic arguments)唠雕。更加詳細(xì)的說(shuō)明見(jiàn)官方文檔贸营。
安裝方法就4種:
一. Dynamic Framework
注意:動(dòng)態(tài)框架與 iOS 7 不兼容,要支持 iOS 7 的話(huà)請(qǐng)查看“靜態(tài)框架”岩睁。
下載最新的Realm發(fā)行版本钞脂,并解壓;
前往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按鈕;
在單元測(cè)試 Target 的”Build Settings”中栋猖,在”Framework Search Paths”中添加Realm.framework的上級(jí)目錄净薛;
如果希望使用 Swift 加載 Realm,請(qǐng)拖動(dòng)Swift/RLMSupport.swift文件到 Xcode 工程的文件導(dǎo)航欄中并選中Copy items if needed蒲拉;
如果在 iOS肃拜、watchOS 或者 tvOS 項(xiàng)目中使用 Realm,請(qǐng)?jiān)谀鷳?yīng)用目標(biāo)的”Build Phases”中雌团,創(chuàng)建一個(gè)新的”Run Script Phase”燃领,并將
bash "${BUILT_PRODUCTS_DIR}/${FRAMEWORKS_FOLDER_PATH}/Realm.framework/strip-frameworks.sh"
這條腳本復(fù)制到文本框中。 因?yàn)橐@過(guò)APP商店提交的bug锦援,這一步在打包通用設(shè)備的二進(jìn)制發(fā)布版本時(shí)是必須的猛蔽。
二.CocoaPods
在項(xiàng)目的Podfile中,添加pod 'Realm'灵寺,在終端運(yùn)行pod install曼库。
三.Carthage
1.在Carthage 中添加github "realm/realm-cocoa",運(yùn)行carthage update略板。為了修改用以構(gòu)建項(xiàng)目的 Swift toolchain毁枯,通過(guò)--toolchain參數(shù)來(lái)指定合適的 toolchain。--no-use-binaries參數(shù)也是必需的叮称,這可以避免 Carthage 將預(yù)構(gòu)建的 Swift 3.0 二進(jìn)制包下載下來(lái)种玛。 例如:
carthage update --toolchain com.apple.dt.toolchain.Swift_2_3 --no-use-binaries
2.從 Carthage/Build/目錄下對(duì)應(yīng)平臺(tái)文件夾中,將 Realm.framework拖曳到您 Xcode 工程”General”設(shè)置項(xiàng)的”Linked Frameworks and Libraries”選項(xiàng)卡中瓤檐;
3.iOS/tvOS/watchOS: 在您應(yīng)用目標(biāo)的“Build Phases”設(shè)置選項(xiàng)卡中赂韵,點(diǎn)擊“+”按鈕并選擇“New Run Script Phase”。在新建的Run Script中距帅,填寫(xiě):
/usr/local/bin/carthage copy-frameworks
在“Input Files”內(nèi)添加您想要使用的框架路徑右锨,例如:
$(SRCROOT)/Carthage/Build/iOS/Realm.framework
因?yàn)橐@過(guò)APP商店提交的bug,這一步在打包通用設(shè)備的二進(jìn)制發(fā)布版本時(shí)是必須的碌秸。
四.Static Framework (iOS only)
下載 Realm 的最新版本并解壓绍移,將 Realm.framework 從 ios/static/文件夾拖曳到您 Xcode 項(xiàng)目中的文件導(dǎo)航器當(dāng)中。確保 Copy items if needed 選中然后單擊 Finish讥电;
在 Xcode 文件導(dǎo)航器中選擇您的項(xiàng)目蹂窖,然后選擇您的應(yīng)用目標(biāo),進(jìn)入到** Build Phases** 選項(xiàng)卡中恩敌。在 Link Binary with Libraries 中單擊 + 號(hào)然后添加libc++.dylib瞬测;
二. Realm 中的相關(guān)術(shù)語(yǔ)
為了能更好的理解Realm的使用,先介紹一下涉及到的相關(guān)術(shù)語(yǔ)。
RLMRealm:Realm是框架的核心所在月趟,是我們構(gòu)建數(shù)據(jù)庫(kù)的訪問(wèn)點(diǎn)灯蝴,就如同Core Data的管理對(duì)象上下文(managed object context)一樣。出于簡(jiǎn)單起見(jiàn)孝宗,realm提供了一個(gè)默認(rèn)的defaultRealm( )的便利構(gòu)造器方法穷躁。
RLMObject:這是我們自定義的Realm數(shù)據(jù)模型。創(chuàng)建數(shù)據(jù)模型的行為對(duì)應(yīng)的就是數(shù)據(jù)庫(kù)的結(jié)構(gòu)因妇。要?jiǎng)?chuàng)建一個(gè)數(shù)據(jù)模型问潭,我們只需要繼承RLMObject,然后設(shè)計(jì)我們想要存儲(chǔ)的屬性即可婚被。
關(guān)系(Relationships):通過(guò)簡(jiǎn)單地在數(shù)據(jù)模型中聲明一個(gè)RLMObject類(lèi)型的屬性狡忙,我們就可以創(chuàng)建一個(gè)“一對(duì)多”的對(duì)象關(guān)系。同樣地址芯,我們還可以創(chuàng)建“多對(duì)一”和“多對(duì)多”的關(guān)系灾茁。
寫(xiě)操作事務(wù)(Write Transactions):數(shù)據(jù)庫(kù)中的所有操作,比如創(chuàng)建是复、編輯删顶,或者刪除對(duì)象,都必須在事務(wù)中完成淑廊《河啵“事務(wù)”是指位于write閉包內(nèi)的代碼段。
查詢(xún)(Queries):要在數(shù)據(jù)庫(kù)中檢索信息季惩,我們需要用到“檢索”操作录粱。檢索最簡(jiǎn)單的形式是對(duì)Realm( )數(shù)據(jù)庫(kù)發(fā)送查詢(xún)消息。如果需要檢索更復(fù)雜的數(shù)據(jù)画拾,那么還可以使用斷言(predicates)啥繁、復(fù)合查詢(xún)以及結(jié)果排序等等操作。
RLMResults:這個(gè)類(lèi)是執(zhí)行任何查詢(xún)請(qǐng)求后所返回的類(lèi)青抛,其中包含了一系列的RLMObject對(duì)象旗闽。RLMResults和NSArray類(lèi)似,我們可以用下標(biāo)語(yǔ)法來(lái)對(duì)其進(jìn)行訪問(wèn)蜜另,并且還可以決定它們之間的關(guān)系适室。不僅如此,它還擁有許多更強(qiáng)大的功能举瑰,包括排序捣辆、查找等等操作。
三.Realm 入門(mén)——如何使用
由于Realm的API極為友好此迅,一看就懂汽畴,所以這里就按照平時(shí)開(kāi)發(fā)的順序旧巾,把需要用到的都梳理一遍。
-
創(chuàng)建數(shù)據(jù)庫(kù)
- (void)creatDataBaseWithName:(NSString *)databaseName{ NSArray *docPath = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES); NSString *path = [docPath objectAtIndex:0]; NSString *filePath = [path stringByAppendingPathComponent:databaseName]; NSLog(@"數(shù)據(jù)庫(kù)目錄 = %@",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) { // 這里是設(shè)置數(shù)據(jù)遷移的block if (oldSchemaVersion < currentVersion) { } }; [RLMRealmConfiguration setDefaultConfiguration:config];}
創(chuàng)建數(shù)據(jù)庫(kù)主要設(shè)置RLMRealmConfiguration忍些,設(shè)置數(shù)據(jù)庫(kù)名字和存儲(chǔ)地方鲁猩。把路徑以及數(shù)據(jù)庫(kù)名字拼接好字符串,賦值給fileURL即可坐昙。
objectClasses這個(gè)屬性是用來(lái)控制對(duì)哪個(gè)類(lèi)能夠存儲(chǔ)在指定 Realm 數(shù)據(jù)庫(kù)中做出限制绳匀。例如,如果有兩個(gè)團(tuán)隊(duì)分別負(fù)責(zé)開(kāi)發(fā)您應(yīng)用中的不同部分炸客,并且同時(shí)在應(yīng)用內(nèi)部使用了 Realm 數(shù)據(jù)庫(kù),那么您肯定不希望為它們協(xié)調(diào)進(jìn)行數(shù)據(jù)遷移您可以通過(guò)設(shè)置RLMRealmConfiguration的 objectClasses屬性來(lái)對(duì)類(lèi)做出限制戈钢。objectClasses一般可以不用設(shè)置痹仙。
readOnly是控制是否只讀屬性。
還有一個(gè)很特殊的數(shù)據(jù)庫(kù)殉了,內(nèi)存數(shù)據(jù)庫(kù)开仰。
通常情況下,Realm 數(shù)據(jù)庫(kù)是存儲(chǔ)在硬盤(pán)中的薪铜,但是您能夠通過(guò)設(shè)置inMemoryIdentifier而不是設(shè)置RLMRealmConfiguration中的 fileURL屬性众弓,以創(chuàng)建一個(gè)完全在內(nèi)存中運(yùn)行的數(shù)據(jù)庫(kù)。
RLMRealmConfiguration *config = [RLMRealmConfiguration defaultConfiguration];
config.inMemoryIdentifier = @"MyInMemoryRealm";
RLMRealm *realm = [RLMRealm realmWithConfiguration:config error:nil];
內(nèi)存數(shù)據(jù)庫(kù)在每次程序運(yùn)行期間都不會(huì)保存數(shù)據(jù)隔箍。但是谓娃,這不會(huì)妨礙到 Realm 的其他功能,包括查詢(xún)蜒滩、關(guān)系以及線(xiàn)程安全滨达。
如果需要一種靈活的數(shù)據(jù)讀寫(xiě)但又不想儲(chǔ)存數(shù)據(jù)的方式的話(huà),那么可以選擇用內(nèi)存數(shù)據(jù)庫(kù)俯艰。(關(guān)于內(nèi)存數(shù)據(jù)庫(kù)的性能 和 類(lèi)屬性的 性能捡遍,還沒(méi)有測(cè)試過(guò),感覺(jué)性能不會(huì)有太大的差異竹握,所以?xún)?nèi)存數(shù)據(jù)庫(kù)使用場(chǎng)景感覺(jué)不多)
使用內(nèi)存數(shù)據(jù)庫(kù)需要注意的是:
內(nèi)存數(shù)據(jù)庫(kù)會(huì)在臨時(shí)文件夾中創(chuàng)建多個(gè)文件画株,用來(lái)協(xié)調(diào)處理諸如跨進(jìn)程通知之類(lèi)的事務(wù)。 實(shí)際上沒(méi)有任何的數(shù)據(jù)會(huì)被寫(xiě)入到這些文件當(dāng)中啦辐,除非操作系統(tǒng)由于內(nèi)存過(guò)滿(mǎn)谓传, 需要清除磁盤(pán)上的多余空間。 才會(huì)去把內(nèi)存里面的數(shù)據(jù)存入到文件中昧甘。(感謝 @酷酷的哀殿 指出)
如果某個(gè)內(nèi)存 Realm 數(shù)據(jù)庫(kù)實(shí)例沒(méi)有被引用良拼,那么所有的數(shù)據(jù)就會(huì)被釋放。所以必須要在應(yīng)用的生命周期內(nèi)保持對(duì)Realm內(nèi)存數(shù)據(jù)庫(kù)的強(qiáng)引用充边,以避免數(shù)據(jù)丟失庸推。
-
建表
Realm數(shù)據(jù)模型是基于標(biāo)準(zhǔn) Objective?C 類(lèi)來(lái)進(jìn)行定義的常侦,使用屬性來(lái)完成模型的具體定義。
我們只需要繼承 RLMObject或者一個(gè)已經(jīng)存在的模型類(lèi)贬媒,您就可以創(chuàng)建一個(gè)新的 Realm 數(shù)據(jù)模型對(duì)象聋亡。對(duì)應(yīng)在數(shù)據(jù)庫(kù)里面就是一張表。#import <Realm/Realm.h> @interface RLMUser : RLMObject @property NSString *accid; //用戶(hù)注冊(cè)id @property NSInteger custId; //姓名 @property NSString *custName; //頭像大圖url @property NSString *avatarBig; @property RLMArray<Car> *cars; RLM_ARRAY_TYPE(RLMUser) // 定義RLMArray<RLMUser> @interface Car : RLMObject @property NSString *carName; @property RLMUser *owner;@endRLM_ARRAY_TYPE(Car) // 定義RLMArray<Car> @end
注意RLMObject 官方建議不要加上 Objective-C的property attributes(如nonatomic, atomic, strong, copy, weak 等等)假如設(shè)置了际乘,這些attributes會(huì)一直生效直到RLMObject被寫(xiě)入realm數(shù)據(jù)庫(kù)坡倔。
RLM_ARRAY_TYPE宏創(chuàng)建了一個(gè)協(xié)議,從而允許 RLMArray<Car>語(yǔ)法的使用脖含。如果該宏沒(méi)有放置在模型接口的底部的話(huà)罪塔,您或許需要提前聲明該模型類(lèi)。
關(guān)于RLMObject的的關(guān)系
1.對(duì)一(To-One)關(guān)系
對(duì)于多對(duì)一(many-to-one)或者一對(duì)一(one-to-one)關(guān)系來(lái)說(shuō)养葵,只需要聲明一個(gè)RLMObject子類(lèi)類(lèi)型的屬性即可征堪,如上面代碼例子,@property RLMUser *owner;
2.對(duì)多(To-Many)關(guān)系通過(guò) RLMArray類(lèi)型的屬性您可以定義一個(gè)對(duì)多關(guān)系关拒。如上面代碼例子佃蚜,@property RLMArray<Car> *cars;
3.反向關(guān)系(Inverse Relationship)
鏈接是單向性的。因此着绊,如果對(duì)多關(guān)系屬性 RLMUser.cars鏈接了一個(gè) Car實(shí)例谐算,而這個(gè)實(shí)例的對(duì)一關(guān)系屬性 Car.owner又鏈接到了對(duì)應(yīng)的這個(gè) RLMUser實(shí)例,那么實(shí)際上這些鏈接仍然是互相獨(dú)立的归露。
@interface Car : RLMObject
@property NSString *carName;
@property (readonly) RLMLinkingObjects *owners;
@end
@implementation Car
+ (NSDictionary *)linkingObjectsProperties {
return
@{
@"owners": [RLMPropertyDescriptor descriptorWithClass:RLMUser.class propertyName:@"cars"];
};
}
@end
這里可以類(lèi)比Core Data里面xcdatamodel文件里面那些“箭頭”
@implementation Book
// 主鍵
+ (NSString *)primaryKey { return @"ID";}
//設(shè)置屬性默認(rèn)值
+ (NSDictionary *)defaultPropertyValues{ return @{@"carName":@"測(cè)試" };}
//設(shè)置忽略屬性,即不存到realm數(shù)據(jù)庫(kù)中
+ (NSArray<NSString *> *)ignoredProperties { return @[@"ID"];}
//一般來(lái)說(shuō),屬性為nil的話(huà)realm會(huì)拋出異常,但是如果實(shí)現(xiàn)了這個(gè)方法的話(huà),就只有name為nil會(huì)拋出異常,也就是說(shuō)現(xiàn)在cover屬性可以為空了
+ (NSArray *)requiredProperties { return @[@"name"];}
//設(shè)置索引,可以加快檢索的速度
+ (NSArray *)indexedProperties { return @[@"ID"];
}
@end
還可以給RLMObject設(shè)置主鍵primaryKey洲脂,默認(rèn)值defaultPropertyValues,忽略的屬性ignoredProperties靶擦,必要屬性requiredProperties腮考,索引indexedProperties。比較有用的是主鍵和索引玄捕。
3.存儲(chǔ)數(shù)據(jù)
新建對(duì)象
// (1) 創(chuàng)建一個(gè)Car對(duì)象踩蔚,然后設(shè)置其屬性
Car *car = [[Car alloc] init];
car.carName = @"Lamborghini";
// (2) 通過(guò)字典創(chuàng)建Car對(duì)象
Car *myOtherCar = [[Car alloc] initWithValue:@{@"name" : @"Rolls-Royce"}];
// (3) 通過(guò)數(shù)組創(chuàng)建狗狗對(duì)象
Car *myThirdcar = [[Car alloc] initWithValue:@[@"BMW"]];`
注意,所有的必需屬性都必須在對(duì)象添加到 Realm 前被賦值
4.增
[realm beginWriteTransaction];
[realm addObject:Car];
[realm commitWriteTransaction];
請(qǐng)注意枚粘,如果在進(jìn)程中存在多個(gè)寫(xiě)入操作的話(huà)馅闽,那么單個(gè)寫(xiě)入操作將會(huì)阻塞其余的寫(xiě)入操作,并且還會(huì)鎖定該操作所在的當(dāng)前線(xiàn)程馍迄。
Realm這個(gè)特性與其他持久化解決方案類(lèi)似福也,我們建議您使用該方案常規(guī)的最佳做法:將寫(xiě)入操作轉(zhuǎn)移到一個(gè)獨(dú)立的線(xiàn)程中執(zhí)行。
官方給出了一個(gè)建議:
由于 Realm 采用了 MVCC 設(shè)計(jì)架構(gòu)攀圈,讀取操作并不會(huì)因?yàn)閷?xiě)入事務(wù)正在進(jìn)行而受到影響暴凑。除非您需要立即使用多個(gè)線(xiàn)程來(lái)同時(shí)執(zhí)行寫(xiě)入操作,不然您應(yīng)當(dāng)采用批量化的寫(xiě)入事務(wù)赘来,而不是采用多次少量的寫(xiě)入事務(wù)现喳。
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
RLMRealm *realm = [RLMRealm defaultRealm];
[realm transactionWithBlock:^{
[realm addObject: Car];
}];
});`
上面的代碼就是把寫(xiě)事務(wù)放到子線(xiàn)程中去處理凯傲。
5.刪
[realm beginWriteTransaction];// 刪除單條記錄
[realm deleteObject:Car];// 刪除多條記錄
[realm deleteObjects:CarResult];// 刪除所有記錄
[realm deleteAllObjects];
[realm commitWriteTransaction];
6.改
當(dāng)沒(méi)有主鍵的情況下,需要先查詢(xún)嗦篱,再修改數(shù)據(jù)冰单。當(dāng)有主鍵的情況下,有以下幾個(gè)非常好用的API
[realm addOrUpdateObject:Car];
[Car createOrUpdateInRealm:realm withValue:@{@"id": @1, @"price": @9000.0f}];
addOrUpdateObject會(huì)去先查找有沒(méi)有傳入的Car相同的主鍵灸促,如果有诫欠,就更新該條數(shù)據(jù)。這里需要注意浴栽,addOrUpdateObject這個(gè)方法不是增量更新荒叼,所有的值都必須有,如果有哪幾個(gè)值是null吃度,那么就會(huì)覆蓋原來(lái)已經(jīng)有的值甩挫,這樣就會(huì)出現(xiàn)數(shù)據(jù)丟失的問(wèn)題。
createOrUpdateInRealm:withValue:這個(gè)方法是增量更新的椿每,后面?zhèn)饕粋€(gè)字典,使用這個(gè)方法的前提是有主鍵英遭。方法會(huì)先去主鍵里面找有沒(méi)有字典里面?zhèn)魅氲闹麈I的記錄间护,如果有,就只更新字典里面的子集挖诸。如果沒(méi)有汁尺,就新建一條記錄。
7.查
在Realm中所有的查詢(xún)(包括查詢(xún)和屬性訪問(wèn))在 Realm 中都是延遲加載的多律,只有當(dāng)屬性被訪問(wèn)時(shí)痴突,才能夠讀取相應(yīng)的數(shù)據(jù)。
查詢(xún)結(jié)果并不是數(shù)據(jù)的拷貝:修改查詢(xún)結(jié)果(在寫(xiě)入事務(wù)中)會(huì)直接修改硬盤(pán)上的數(shù)據(jù)狼荞。同樣地辽装,您可以直接通過(guò)包含在RLMResults中的RLMObject對(duì)象完成遍歷關(guān)系圖的操作。除非查詢(xún)結(jié)果被使用相味,否則檢索的執(zhí)行將會(huì)被推遲拾积。這意味著鏈接幾個(gè)不同的臨時(shí) {RLMResults} 來(lái)進(jìn)行排序和匹配數(shù)據(jù),不會(huì)執(zhí)行額外的工作丰涉,例如處理中間狀態(tài)拓巧。一旦檢索執(zhí)行之后,或者通知模塊被添加之后一死, RLMResults將隨時(shí)保持更新肛度,接收 Realm 中,在后臺(tái)線(xiàn)程上執(zhí)行的檢索操作中可能所做的更改投慈。
//從默認(rèn)數(shù)據(jù)庫(kù)查詢(xún)所有的車(chē)
RLMResults<Car *> *cars = [Car allObjects];
// 使用斷言字符串查詢(xún)
RLMResults<Dog *> *tanDogs = [Dog objectsWhere:@"color = '棕黃色' AND name BEGINSWITH '大'"];
// 使用 NSPredicate 查詢(xún)
NSPredicate *pred = [NSPredicate predicateWithFormat:@"color = %@ AND name BEGINSWITH %@", @"棕黃色", @"大"];
RLMResults *results = [Dog objectsWithPredicate:pred];
// 排序名字以“大”開(kāi)頭的棕黃色狗狗
RLMResults<Dog *> *sortedDogs = [[Dog objectsWhere:@"color = '棕黃色' AND name BEGINSWITH '大'"] sortedResultsUsingProperty:@"name" ascending:YES];
Realm還能支持鏈?zhǔn)讲樵?xún)
Realm 查詢(xún)引擎一個(gè)特性就是它能夠通過(guò)非常小的事務(wù)開(kāi)銷(xiāo)來(lái)執(zhí)行鏈?zhǔn)讲樵?xún)(chain queries)承耿,而不需要像傳統(tǒng)數(shù)據(jù)庫(kù)那樣為每個(gè)成功的查詢(xún)創(chuàng)建一個(gè)不同的數(shù)據(jù)庫(kù)服務(wù)器訪問(wèn)冠骄。
RLMResults<Car *> *Cars = [Car objectsWhere:@"color = blue"];
RLMResults<Car *> *CarsWithBNames = [Cars objectsWhere:@"name BEGINSWITH 'B'"];`
8.其他相關(guān)特性
1.支持KVC和KVO
RLMObject、RLMResult以及 RLMArray都遵守鍵值編碼(Key-Value Coding)(KVC)機(jī)制瘩绒。當(dāng)您在運(yùn)行時(shí)才能決定哪個(gè)屬性需要更新的時(shí)候猴抹,這個(gè)方法是最有用的。將 KVC 應(yīng)用在集合當(dāng)中是大量更新對(duì)象的極佳方式锁荔,這樣就可以不用經(jīng)常遍歷集合蟀给,為每個(gè)項(xiàng)目創(chuàng)建一個(gè)訪問(wèn)器了。
RLMResults<Person *> *persons = [Person allObjects];
[[RLMRealm defaultRealm] transactionWithBlock:^{
[[persons firstObject] setValue:@YES forKeyPath:@"isFirst"];
// 將每個(gè)人的 planet 屬性設(shè)置為“地球”
[persons setValue:@"地球" forKeyPath:@"planet"];}];
Realm 對(duì)象的大多數(shù)屬性都遵從 KVO 機(jī)制阳堕。所有 RLMObject子類(lèi)的持久化(persisted)存儲(chǔ)(未被忽略)的屬性都是遵循 KVO 機(jī)制的跋理,并且 RLMObject以及 RLMArray中 無(wú)效的(invalidated)屬性也同樣遵循(然而 RLMLinkingObjects屬性并不能使用 KVO 進(jìn)行觀察)。
2.支持?jǐn)?shù)據(jù)庫(kù)加密
// 產(chǎn)生隨機(jī)密鑰
NSMutableData *key = [NSMutableData dataWithLength:64];
SecRandomCopyBytes(kSecRandomDefault, key.length, (uint8_t *)key.mutableBytes);
// 打開(kāi)加密文件
RLMRealmConfiguration *config = [RLMRealmConfiguration defaultConfiguration];
config.encryptionKey = key;
NSError *error = nil;
RLMRealm *realm = [RLMRealm realmWithConfiguration:config error:&error];
if (!realm) {
// 如果密鑰錯(cuò)誤恬总,`error` 會(huì)提示數(shù)據(jù)庫(kù)不可訪問(wèn)
NSLog(@"Error opening realm: %@", error);}
Realm 支持在創(chuàng)建 Realm 數(shù)據(jù)庫(kù)時(shí)采用64位的密鑰對(duì)數(shù)據(jù)庫(kù)文件進(jìn)行 AES-256+SHA2 加密前普。這樣硬盤(pán)上的數(shù)據(jù)都能都采用AES-256來(lái)進(jìn)行加密和解密,并用 SHA-2 HMAC 來(lái)進(jìn)行驗(yàn)證壹堰。每次您要獲取一個(gè) Realm 實(shí)例時(shí)拭卿,您都需要提供一次相同的密鑰。
不過(guò)贱纠,加密過(guò)的 Realm 只會(huì)帶來(lái)很少的額外資源占用(通常最多只會(huì)比平常慢10%)峻厚。
3.通知
// 獲取 Realm 通知
token = [realm addNotificationBlock:^(NSString *notification,RLMRealm * realm) { [myViewController updateUI];}];
[token stop];
// 移除通知
[realm removeNotification:self.token];
Realm 實(shí)例將會(huì)在每次寫(xiě)入事務(wù)提交后,給其他線(xiàn)程上的 Realm 實(shí)例發(fā)送通知谆焊。一般控制器如果想一直持有這個(gè)通知惠桃,就需要申請(qǐng)一個(gè)屬性,strong持有這個(gè)通知辖试。
- (void)viewDidLoad { [super viewDidLoad];
// 觀察 RLMResults 通知
__weak typeof(self) weakSelf = self;
self.notificationToken = [[Person objectsWhere:@"age > 5"]
addNotificationBlock:^(RLMResults<Person *> *results, RLMCollectionChange *change, NSError *error) {
if (error) {
NSLog(@"Failed to open Realm on background worker: %@", error); return; }
UITableView *tableView = weakSelf.tableView;
// 對(duì)于變化信息來(lái)說(shuō)辜王,檢索的初次運(yùn)行將會(huì)傳遞 nil
if (!changes) {
[tableView reloadData];
return;
}
// 檢索結(jié)果被改變,因此將它們應(yīng)用到 UITableView 當(dāng)中
[tableView beginUpdates];
[tableView deleteRowsAtIndexPaths:[changes deletionsInSection:0]
withRowAnimation:UITableViewRowAnimationAutomatic];
[tableView insertRowsAtIndexPaths:[changes insertionsInSection:0]
withRowAnimation:UITableViewRowAnimationAutomatic];
[tableView reloadRowsAtIndexPaths:[changes modificationsInSection:0]
withRowAnimation:UITableViewRowAnimationAutomatic];
[tableView endUpdates];
}];
}
我們還能進(jìn)行更加細(xì)粒度的通知罐孝,用集合通知就可以做到呐馆。
集合通知是異步觸發(fā)的,首先它會(huì)在初始結(jié)果出現(xiàn)的時(shí)候觸發(fā)肾档,隨后當(dāng)某個(gè)寫(xiě)入事務(wù)改變了集合中的所有或者某個(gè)對(duì)象的時(shí)候摹恰,通知都會(huì)再次觸發(fā)。這些變化可以通過(guò)傳遞到通知閉包當(dāng)?shù)?RLMCollectionChange參數(shù)訪問(wèn)到怒见。這個(gè)對(duì)象當(dāng)中包含了受 deletions俗慈、insertions和 modifications 狀態(tài)所影響的索引信息。
集合通知對(duì)于 RLMResults遣耍、RLMArray闺阱、RLMLinkingObjects 以及 RLMResults 這些衍生出來(lái)的集合來(lái)說(shuō),當(dāng)關(guān)系中的對(duì)象被添加或者刪除的時(shí)候舵变,一樣也會(huì)觸發(fā)這個(gè)狀態(tài)變化酣溃。
4.數(shù)據(jù)庫(kù)遷移
這是Realm的優(yōu)點(diǎn)之一瘦穆,方便遷移。
對(duì)比Core Data的數(shù)據(jù)遷移赊豌,實(shí)在是方便太多了扛或。關(guān)于iOS Core Data 數(shù)據(jù)遷移 指南請(qǐng)看這篇文章。
數(shù)據(jù)庫(kù)存儲(chǔ)方面的增刪改查應(yīng)該都沒(méi)有什么大問(wèn)題碘饼,比較蛋疼的應(yīng)該就是數(shù)據(jù)遷移了熙兔。在版本迭代過(guò)程中,很可能會(huì)發(fā)生表的新增艾恼,刪除住涉,或者表結(jié)構(gòu)的變化,如果新版本中不做數(shù)據(jù)遷移钠绍,用戶(hù)升級(jí)到新版舆声,很可能就直接crash了。對(duì)比Core Data的數(shù)據(jù)遷移比較復(fù)雜柳爽,Realm的遷移實(shí)在太簡(jiǎn)單了媳握。
1.新增刪除表,Realm不需要做遷移2.新增刪除字段磷脯,Realm不需要做遷移毙芜。Realm 會(huì)自行檢測(cè)新增和需要移除的屬性,然后自動(dòng)更新硬盤(pán)上的數(shù)據(jù)庫(kù)架構(gòu)争拐。
舉個(gè)官方給的數(shù)據(jù)遷移的例子:
RLMRealmConfiguration *config = [RLMRealmConfiguration defaultConfiguration];
config.schemaVersion = 2;
config.migrationBlock = ^(RLMMigration *migration, uint64_t oldSchemaVersion){
// enumerateObjects:block: 遍歷了存儲(chǔ)在 Realm 文件中的每一個(gè)“Person”對(duì)象
[migration enumerateObjects:Person.className block:^(RLMObject *oldObject, RLMObject *newObject) {
// 只有當(dāng) Realm 數(shù)據(jù)庫(kù)的架構(gòu)版本為 0 的時(shí)候,才添加 “fullName” 屬性
if (oldSchemaVersion < 1) {
newObject[@"fullName"] = [NSString stringWithFormat:
@"%@ %@", oldObject[@"firstName"], oldObject[@"lastName"]];
}
// 只有當(dāng) Realm 數(shù)據(jù)庫(kù)的架構(gòu)版本為 0 或者 1 的時(shí)候晦雨,才添加“email”屬性
if (oldSchemaVersion < 2) {
newObject[@"email"] = @"";
}
// 替換屬性名
if (oldSchemaVersion < 3) {
// 重命名操作應(yīng)該在調(diào)用 `enumerateObjects:` 之外完成
[migration renamePropertyForClass:Person.className oldName:@"yearsSinceBirth" newName:@"age"];
} }];
};
[RLMRealmConfiguration setDefaultConfiguration:config];
// 現(xiàn)在我們已經(jīng)成功更新了架構(gòu)版本并且提供了遷移閉包架曹,打開(kāi)舊有的 Realm 數(shù)據(jù)庫(kù)會(huì)自動(dòng)執(zhí)行此數(shù)據(jù)遷移,然后成功進(jìn)行訪問(wèn)
[RLMRealm defaultRealm];
在block里面分別有3種遷移方式闹瞧,第一種是合并字段的例子绑雄,第二種是增加新字段的例子,第三種是原字段重命名的例子奥邮。
四. Realm 使用中可能需要注意的一些問(wèn)題
在我從0開(kāi)始接觸Realm到熟練上手万牺,基本就遇到了多線(xiàn)程這一個(gè)坑非剃∪たⅲ可見(jiàn)Realm的API文檔是多么的友好。雖然坑不多骇塘,但是還有有些需要注意的地方蘸朋。
1.跨線(xiàn)程訪問(wèn)數(shù)據(jù)庫(kù)核无,Realm對(duì)象一定需要新建一個(gè)
*** Terminating app due to uncaught exception 'RLMException', reason: 'Realm accessed from incorrect thread.`
******* First throw call stack:
****(**** 0 CoreFoundation 0x000000011479f34b __exceptionPreprocess + 171
**** 1 libobjc.A.dylib 0x00000001164a321e objc_exception_throw + 48
**** 2 BHFangChuang 0x000000010dd4c2b5 -[RLMRealm beginWriteTransaction] + 77
**** 3 BHFangChuang 0x000000010dd4c377 -[RLMRealm transactionWithBlock:error:] + 45
**** 4 BHFangChuang 0x000000010dd4c348 -[RLMRealm transactionWithBlock:] + 19
**** 5 BHFangChuang 0x000000010d51d7ae __71-[RealmDataBaseHelper updateUserWithLoginDate:andLogoutDate:according:]_block_invoke + 190
**** 6 libdispatch.dylib 0x00000001180ef980 _dispatch_call_block_and_release + 12
**** 7 libdispatch.dylib 0x00000001181190cd _dispatch_client_callout + 8
**** 8 libdispatch.dylib 0x00000001180f8366 _dispatch_queue_override_invoke + 1426
**** 9 libdispatch.dylib 0x00000001180fa3b7 _dispatch_root_queue_drain + 720
**** 10 libdispatch.dylib 0x00000001180fa08b _dispatch_worker_thread3 + 123
**** 11 libsystem_pthread.dylib 0x00000001184c8746 _pthread_wqthread + 1299
**** 12 libsystem_pthread.dylib 0x00000001184c8221 start_wqthread +
13****)****libc++abi.dylib: terminating with uncaught exception of type NSException**
如果程序崩潰了,出現(xiàn)以上錯(cuò)誤藕坯,那就是因?yàn)槟阍L問(wèn)Realm數(shù)據(jù)的時(shí)候团南,使用的Realm對(duì)象所在的線(xiàn)程和當(dāng)前線(xiàn)程不一致噪沙。
解決辦法就是在當(dāng)前線(xiàn)程重新獲取最新的Realm,即可吐根。
-
自己封裝一個(gè)Realm全局實(shí)例單例是沒(méi)啥作用的
這個(gè)也是我之前對(duì)Realm多線(xiàn)程理解不清正歼,導(dǎo)致的一個(gè)誤解。
很多開(kāi)發(fā)者應(yīng)該都會(huì)對(duì)Core Data和Sqlite3或者FMDB拷橘,自己封裝一個(gè)類(lèi)似Helper的單例局义。于是我也在這里封裝了一個(gè)單例,在新建完Realm數(shù)據(jù)庫(kù)的時(shí)候strong持有一個(gè)Realm的對(duì)象膜楷。然后之后的訪問(wèn)中只需要讀取這個(gè)單例持有的Realm對(duì)象就可以拿到數(shù)據(jù)庫(kù)了旭咽。
想法是好的,但是同一個(gè)Realm對(duì)象是不支持跨線(xiàn)程操作realm數(shù)據(jù)庫(kù)的赌厅。
Realm 通過(guò)確保每個(gè)線(xiàn)程始終擁有 Realm 的一個(gè)快照穷绵,以便讓并發(fā)運(yùn)行變得十分輕松。你可以同時(shí)有任意數(shù)目的線(xiàn)程訪問(wèn)同一個(gè) Realm 文件特愿,并且由于每個(gè)線(xiàn)程都有對(duì)應(yīng)的快照仲墨,因此線(xiàn)程之間絕不會(huì)產(chǎn)生影響。需要注意的一件事情就是不能讓多個(gè)線(xiàn)程都持有同一個(gè) Realm 對(duì)象的 實(shí)例 揍障。如果多個(gè)線(xiàn)程需要訪問(wèn)同一個(gè)對(duì)象目养,那么它們分別會(huì)獲取自己所需要的實(shí)例(否則在一個(gè)線(xiàn)程上發(fā)生的更改就會(huì)造成其他線(xiàn)程得到不完整或者不一致的數(shù)據(jù))。
其實(shí)RLMRealm *realm = [RLMRealm defaultRealm]; 這句話(huà)就是獲取了當(dāng)前realm對(duì)象的一個(gè)實(shí)例毒嫡,其實(shí)實(shí)現(xiàn)就是拿到單例癌蚁。所以我們每次在子線(xiàn)程里面不要再去讀取我們自己封裝持有的realm實(shí)例了,直接調(diào)用系統(tǒng)的這個(gè)方法即可兜畸,能保證訪問(wèn)不出錯(cuò)努释。
3.transactionWithBlock 已經(jīng)處于一個(gè)寫(xiě)的事務(wù)中,事務(wù)之間不能嵌套[realm transactionWithBlock:^{ [self.realm beginWriteTransaction]; [self convertToRLMUserWith:bhUser To:[self convertToRLMUserWith:bhUser To:nil]]; [self.realm commitWriteTransaction]; }];
transactionWithBlock 已經(jīng)處于一個(gè)寫(xiě)的事務(wù)中咬摇,如果還在block里面再寫(xiě)一個(gè)commitWriteTransaction伐蒂,就會(huì)出錯(cuò),寫(xiě)事務(wù)是不能嵌套的肛鹏。
出錯(cuò)信息如下:
*** Terminating app due to uncaught exception 'RLMException', reason: 'The Realm is already in a write transaction'
******* First throw call stack:****(**** 0 CoreFoundation 0x0000000112e2d34b __exceptionPreprocess + 171
**** 1 libobjc.A.dylib 0x0000000114b3121e objc_exception_throw + 48
**** 2 BHFangChuang 0x000000010c4702b5 -[RLMRealm beginWriteTransaction] + 77
**** 3 BHFangChuang 0x000000010bc4175a __71-[RealmDataBaseHelper updateUserWithLoginDate:andLogoutDate:according:]_block_invoke_2 + 42
**** 4 BHFangChuang 0x000000010c470380 -[RLMRealm transactionWithBlock:error:] + 54
**** 5 BHFangChuang 0x000000010c470348 -[RLMRealm transactionWithBlock:] + 19
**** 6 BHFangChuang 0x000000010bc416d7 __71-[RealmDataBaseHelper updateUserWithLoginDate:andLogoutDate:according:]_block_invoke + 231
**** 7 libdispatch.dylib 0x0000000116819980 _dispatch_call_block_and_release + 12
**** 8 libdispatch.dylib 0x00000001168430cd _dispatch_client_callout + 8
**** 9 libdispatch.dylib 0x0000000116822366 _dispatch_queue_override_invoke + 1426
**** 10 libdispatch.dylib 0x00000001168243b7 _dispatch_root_queue_drain + 720
**** 11 libdispatch.dylib 0x000000011682408b _dispatch_worker_thread3 + 123
**** 12 libsystem_pthread.dylib 0x0000000116bed746 _pthread_wqthread + 1299
**** 13 libsystem_pthread.dylib 0x0000000116bed221 start_wqthread + 13****)****libc++abi.dylib: terminating with uncaught exception of type NSException**
4.建議每個(gè)model都需要設(shè)置主鍵逸邦,這樣可以方便add和update
如果能設(shè)置主鍵,請(qǐng)盡量設(shè)置主鍵在扰,因?yàn)檫@樣方便我們更新數(shù)據(jù)缕减,我們可以很方便的調(diào)用addOrUpdateObject: 或者 createOrUpdateInRealm:withValue:方法進(jìn)行更新。這樣就不需要先根據(jù)主鍵健田,查詢(xún)出數(shù)據(jù)烛卧,然后再去更新。有了主鍵以后,這兩步操作可以一步完成总放。
5.查詢(xún)也不能跨線(xiàn)程查詢(xún)
RLMResults * results = [self selectUserWithAccid:bhUser.accid];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
RLMRealm *realm = [RLMRealm defaultRealm];
[realm transactionWithBlock:^{
[realm addOrUpdateObject:results[0]];
}];
});
由于查詢(xún)是在子線(xiàn)程外查詢(xún)的呈宇,所以跨線(xiàn)程也會(huì)出錯(cuò),出錯(cuò)信息如下:
***** Terminating app due to uncaught exception 'RLMException', reason: 'Realm accessed from incorrect thread'******* First throw call stack:****(**** 0 CoreFoundation 0x000000011517a34b __exceptionPreprocess + 171
**** 1 libobjc.A.dylib 0x0000000116e7e21e objc_exception_throw + 48
**** 2 BHFangChuang 0x000000010e7c34ab _ZL10throwErrorP8NSString + 129
**** 3 BHFangChuang 0x000000010e7c177f -[RLMResults count] + 40
**** 4 BHFangChuang 0x000000010df8f3bf -[RealmDataBaseHelper convertToRLMUserWith:LoginDate:LogoutDate:To:] + 159
**** 5 BHFangChuang 0x000000010df8efc1 __71-[RealmDataBaseHelper updateUserWithLoginDate:andLogoutDate:according:]_block_invoke_2 + 81
**** 6 BHFangChuang 0x000000010e7bd320 -[RLMRealm transactionWithBlock:error:] + 54
**** 7 BHFangChuang 0x000000010e7bd2e8 -[RLMRealm transactionWithBlock:] + 19
**** 8 BHFangChuang 0x000000010df8eecf __71-[RealmDataBaseHelper updateUserWithLoginDate:andLogoutDate:according:]_block_invoke + 351
**** 9 libdispatch.dylib 0x0000000118b63980 _dispatch_call_block_and_release + 12
**** 10 libdispatch.dylib 0x0000000118b8d0cd _dispatch_client_callout + 8
**** 11 libdispatch.dylib 0x0000000118b6c366 _dispatch_queue_override_invoke + 1426
**** 12 libdispatch.dylib 0x0000000118b6e3b7 _dispatch_root_queue_drain + 720
**** 13 libdispatch.dylib 0x0000000118b6e08b _dispatch_worker_thread3 + 123
**** 14 libsystem_pthread.dylib 0x0000000118f3c746 _pthread_wqthread + 1299
**** 15 libsystem_pthread.dylib 0x0000000118f3c221 start_wqthread + 13****)****libc++abi.dylib: terminating with uncaught exception of type **
五. Realm “放棄”——優(yōu)點(diǎn)和缺點(diǎn)
關(guān)于Realm的優(yōu)點(diǎn)局雄,在官網(wǎng)上也說(shuō)了很多了甥啄,我感觸最深的3個(gè)優(yōu)點(diǎn)也在文章開(kāi)頭提到了。
CoreData VS Realm 的對(duì)比炬搭,可以看看這篇文章
說(shuō)到使用 Realm最后的二道門(mén)檻蜈漓,一是如何從其他數(shù)據(jù)庫(kù)遷移到Realm,二是Realm數(shù)據(jù)庫(kù)的一些限制宫盔。
接下來(lái)請(qǐng)還在考慮是否使用Realm的同學(xué)仔細(xì)看清楚融虽,下面是你需要權(quán)衡是否要換到Realm數(shù)據(jù)庫(kù)的重要標(biāo)準(zhǔn)。(以下描述基于Realm最新版 2.0.2)
1.從其他數(shù)據(jù)庫(kù)遷移到Realm
如果從其他數(shù)據(jù)庫(kù)遷移到Realm灼芭,請(qǐng)看我之前寫(xiě)過(guò)的一篇文章有额,簡(jiǎn)單的提一下蛋疼的問(wèn)題,由于切換了數(shù)據(jù)庫(kù)彼绷,需要在未來(lái)幾個(gè)版本都必須維護(hù)2套數(shù)據(jù)庫(kù)巍佑,因?yàn)槔嫌脩?hù)的數(shù)據(jù)需要慢慢從老數(shù)據(jù)庫(kù)遷移到Realm,這個(gè)有點(diǎn)蛋疼寄悯。遷移數(shù)據(jù)的那段代碼需要“惡心”的存在工程里萤衰。但是一旦都遷移完成,之后的路就比較平坦了猜旬。
關(guān)于Core Data遷移過(guò)來(lái)沒(méi)有fetchedResultController的問(wèn)題脆栋,這里提一下。由于使用Realm的話(huà)就無(wú)法使用Core Data的fetchedResultController洒擦,那么如果數(shù)據(jù)庫(kù)更新了數(shù)據(jù)筹吐,是不是只能通過(guò)reloadData來(lái)更新tableview了?目前基本上是的秘遏,Realm提供了我們通知機(jī)制,目前的Realm支持給realm數(shù)據(jù)庫(kù)對(duì)象添加通知嘉竟,這樣就可以在數(shù)據(jù)庫(kù)寫(xiě)入事務(wù)提交后獲取到邦危,從而更新UI;詳情可以參考https://realm.io/cn/docs/swift/latest/#notification當(dāng)然如果仍希望使用NSFetchedResultsController的話(huà)舍扰,那么推薦使用RBQFetchedResultsController倦蚪,這是一個(gè)替代品,地址是:https://github.com/Roobiq/RBQFetchedResultsController目前Realm計(jì)劃在未來(lái)實(shí)現(xiàn)類(lèi)似的效果边苹,具體您可以參見(jiàn)這個(gè)PR:http://github.com/realm/realm-cocoa/issues/687陵且。
當(dāng)然,如果是新的App,還在開(kāi)發(fā)中慕购,可以考慮直接使用Realm聊疲,會(huì)更爽。
以上是第一道門(mén)檻沪悲,如果覺(jué)得遷移帶來(lái)的代價(jià)還能承受获洲,那么恭喜你,已經(jīng)踏入Realm一半了殿如。那么還請(qǐng)看第二道“門(mén)檻”贡珊。
-
Realm數(shù)據(jù)庫(kù)當(dāng)前版本的限制
把用戶(hù)一部分?jǐn)r在Realm門(mén)口的還在這第二道坎,因?yàn)檫@些限制涉馁,這些“缺點(diǎn)”门岔,導(dǎo)致App的業(yè)務(wù)無(wú)法使用Realm得到滿(mǎn)足,所以最終放棄了Realm烤送。當(dāng)然寒随,這些問(wèn)題,有些是可以靈活通過(guò)改變表結(jié)構(gòu)解決的胯努,畢竟人是活的(如果真的想用Realm牢裳,想些辦法,誰(shuí)也攔不滓杜妗)
1.類(lèi)名稱(chēng)的長(zhǎng)度最大只能存儲(chǔ) 57 個(gè) UTF8 字符蒲讯。
2.屬性名稱(chēng)的長(zhǎng)度最大只能支持 63 個(gè) UTF8 字符。
3.NSData以及 NSString屬性不能保存超過(guò) 16 MB 大小的數(shù)據(jù)灰署。如果要存儲(chǔ)大量的數(shù)據(jù)判帮,可通過(guò)將其分解為16MB 大小的塊,或者直接存儲(chǔ)在文件系統(tǒng)中溉箕,然后將文件路徑存儲(chǔ)在 Realm 中晦墙。如果您的應(yīng)用試圖存儲(chǔ)一個(gè)大于 16MB 的單一屬性,系統(tǒng)將在運(yùn)行時(shí)拋出異常肴茄。
4.對(duì)字符串進(jìn)行排序以及不區(qū)分大小寫(xiě)查詢(xún)只支持“基礎(chǔ)拉丁字符集”晌畅、“拉丁字符補(bǔ)充集”、“拉丁文擴(kuò)展字符集 A” 以及”拉丁文擴(kuò)展字符集 B“(UTF-8 的范圍在 0~591 之間)寡痰。
5.盡管 Realm 文件可以被多個(gè)線(xiàn)程同時(shí)訪問(wèn)抗楔,但是您不能跨線(xiàn)程處理 Realms、Realm 對(duì)象拦坠、查詢(xún)和查詢(xún)結(jié)果连躏。(這個(gè)其實(shí)也不算是個(gè)問(wèn)題,我們?cè)诙嗑€(xiàn)程中新建新的Realm對(duì)象就可以解決)
6.Realm對(duì)象的 Setters & Getters 不能被重載
因?yàn)?Realm 在底層數(shù)據(jù)庫(kù)中重寫(xiě)了 setters 和 getters 方法贞滨,所以您不可以在您的對(duì)象上再對(duì)其進(jìn)行重寫(xiě)入热。一個(gè)簡(jiǎn)單的替代方法就是:創(chuàng)建一個(gè)新的 Realm 忽略屬性,該屬性的訪問(wèn)起可以被重寫(xiě), 并且可以調(diào)用其他的 getter 和 setter 方法勺良。
7.文件大小 & 版本跟蹤
一般來(lái)說(shuō) Realm 數(shù)據(jù)庫(kù)比 SQLite 數(shù)據(jù)庫(kù)在硬盤(pán)上占用的空間更少绰播。如果您的 Realm 文件大小超出了您的想象,這可能是因?yàn)槟鷶?shù)據(jù)庫(kù)中的 RLMRealm中包含了舊版本數(shù)據(jù)郑气。為了使您的數(shù)據(jù)有相同的顯示方式幅垮,Realm 只在循環(huán)迭代開(kāi)始的時(shí)候才更新數(shù)據(jù)版本。這意味著尾组,如果您從 Realm 讀取了一些數(shù)據(jù)并進(jìn)行了在一個(gè)鎖定的線(xiàn)程中進(jìn)行長(zhǎng)時(shí)間的運(yùn)行忙芒,然后在其他線(xiàn)程進(jìn)行讀寫(xiě) Realm 數(shù)據(jù)庫(kù)的話(huà),那么版本將不會(huì)被更新讳侨,Realm 將保存中間版本的數(shù)據(jù)呵萨,但是這些數(shù)據(jù)已經(jīng)沒(méi)有用了,這導(dǎo)致了文件大小的增長(zhǎng)跨跨。這部分空間會(huì)在下次寫(xiě)入操作時(shí)被重復(fù)利用潮峦。這些操作可以通過(guò)調(diào)用writeCopyToPath:error:來(lái)實(shí)現(xiàn)。
解決辦法:通過(guò)調(diào)用invalidate勇婴,來(lái)告訴 Realm 您不再需要那些拷貝到 Realm 的數(shù)據(jù)了忱嘹。這可以使我們不必跟蹤這些對(duì)象的中間版本。在下次出現(xiàn)新版本時(shí)耕渴,再進(jìn)行版本更新拘悦。您可能在 Realm 使用Grand Central Dispatch時(shí)也發(fā)現(xiàn)了這個(gè)問(wèn)題。在 dispatch 結(jié)束后自動(dòng)釋放調(diào)度隊(duì)列(dispatch queue)時(shí)橱脸,調(diào)度隊(duì)列(dispatch queue)沒(méi)有隨著程序釋放础米。這造成了直到RLMRealm 對(duì)象被釋放后,Realm 中間版本的數(shù)據(jù)空間才會(huì)被再利用添诉。為了避免這個(gè)問(wèn)題屁桑,您應(yīng)該在 dispatch 隊(duì)列中,使用一個(gè)顯式的自動(dòng)調(diào)度隊(duì)列(dispatch queue)栏赴。
8.Realm 沒(méi)有自動(dòng)增長(zhǎng)屬性
Realm 沒(méi)有線(xiàn)程/進(jìn)程安全的自動(dòng)增長(zhǎng)屬性機(jī)制蘑斧,這在其他數(shù)據(jù)庫(kù)中常常用來(lái)產(chǎn)生主鍵。然而须眷,在絕大多數(shù)情況下乌叶,對(duì)于主鍵來(lái)說(shuō),我們需要的是一個(gè)唯一的柒爸、自動(dòng)生成的值,因此沒(méi)有必要使用順序的事扭、連續(xù)的捎稚、整數(shù)的 ID 作為主鍵。
解決辦法:
在這種情況下,一個(gè)獨(dú)一無(wú)二的字符串主鍵通常就能滿(mǎn)足需求了今野。一個(gè)常見(jiàn)的模式是將默認(rèn)的屬性值設(shè)置為 [[NSUUID UUID] UUIDString]以產(chǎn)生一個(gè)唯一的字符串 ID葡公。自動(dòng)增長(zhǎng)屬性另一種常見(jiàn)的動(dòng)機(jī)是為了維持插入之后的順序。在某些情況下条霜,這可以通過(guò)向某個(gè) RLMArray中添加對(duì)象催什,或者使用 [NSDate date]默認(rèn)值的createdAt屬性。
9.所有的數(shù)據(jù)模型必須直接繼承自RealmObject宰睡。這阻礙我們利用數(shù)據(jù)模型中的任意類(lèi)型的繼承蒲凶。
這一點(diǎn)也不算問(wèn)題,我們只要自己在建立一個(gè)model就可以解決這個(gè)問(wèn)題拆内。自己建立的model可以自己隨意去繼承旋圆,這個(gè)model專(zhuān)門(mén)用來(lái)接收網(wǎng)絡(luò)數(shù)據(jù),然后把自己的這個(gè)model轉(zhuǎn)換成要存儲(chǔ)到表里面的model麸恍,即RLMObject對(duì)象灵巧。這樣這個(gè)問(wèn)題也可以解決了。
Realm 允許模型能夠生成更多的子類(lèi)抹沪,也允許跨模型進(jìn)行代碼復(fù)用刻肄,但是由于某些 Cocoa 特性使得運(yùn)行時(shí)中豐富的類(lèi)多態(tài)無(wú)法使用。以下是可以完成的操作:
父類(lèi)中的類(lèi)方法融欧,實(shí)例方法和屬性可以被它的子類(lèi)所繼承
子類(lèi)中可以在方法以及函數(shù)中使用父類(lèi)作為參數(shù)
以下是不能完成的:
多態(tài)類(lèi)之間的轉(zhuǎn)換(例如子類(lèi)轉(zhuǎn)換成子類(lèi)敏弃,子類(lèi)轉(zhuǎn)換成父類(lèi),父類(lèi)轉(zhuǎn)換成子類(lèi)等)
同時(shí)對(duì)多個(gè)類(lèi)進(jìn)行檢索
多類(lèi)容器 (RLMArray以及 RLMResults)
10.Realm不支持集合類(lèi)型
這一點(diǎn)也是比較蛋疼蹬癌。
Realm支持以下的屬性類(lèi)型:BOOL权她、bool、int逝薪、NSInteger隅要、long、long long董济、float步清、double、NSString虏肾、NSDate廓啊、NSData以及 被特殊類(lèi)型標(biāo)記的NSNumber。CGFloat屬性的支持被取消了封豪,因?yàn)樗痪邆淦脚_(tái)獨(dú)立性谴轮。
這里就是不支持集合,比如說(shuō)NSArray吹埠,NSMutableArray第步,NSDictionary疮装,NSMutableDictionary,NSSet粘都,NSMutableSet廓推。如果服務(wù)器傳來(lái)的一個(gè)字典,key是一個(gè)字符串翩隧,對(duì)應(yīng)的value就是一個(gè)數(shù)組樊展,這時(shí)候就想存儲(chǔ)這個(gè)數(shù)組就比較困難了。當(dāng)然Realm里面是有集合的堆生,就是RLMArray专缠,這里面裝的都是RLMObject。
所以我們想解決這個(gè)問(wèn)題顽频,就需要把數(shù)據(jù)里面的東西都取出來(lái)藤肢,如果是model,就先自己接收一下糯景,然后轉(zhuǎn)換成RLMObject的model嘁圈,再存儲(chǔ)到RLMArray里面去,這樣轉(zhuǎn)換一遍蟀淮,還是可以的做到的最住。
這里列出了暫時(shí)Realm當(dāng)前辦法存在的“缺點(diǎn)”,如果這10點(diǎn)怠惶,在自己的App上都能滿(mǎn)足業(yè)務(wù)需求涨缚,那么這一道坎也不是問(wèn)題了。
以上兩道砍請(qǐng)仔細(xì)衡量清楚策治,這里還有一篇文章是關(guān)于更換數(shù)據(jù)庫(kù)的心得體會(huì)的脓魏,高速公路換輪胎——為遺留系統(tǒng)替換數(shù)據(jù)庫(kù)考慮更換的同學(xué)也可以看看。這兩道坎如果真的不適合通惫,過(guò)不去茂翔,那么請(qǐng)放棄Realm吧!
六. Realm 到底是什么履腋?
大家都知道Sqlite3 是一個(gè)移動(dòng)端上面使用的小型數(shù)據(jù)庫(kù)珊燎,F(xiàn)MDB是基于Sqlite3進(jìn)行的一個(gè)封裝。
那Core Data是數(shù)據(jù)庫(kù)么遵湖?Core Data本身并不是數(shù)據(jù)庫(kù)悔政,它是一個(gè)擁有多種功能的框架,其中一個(gè)重要的功能就是把應(yīng)用程序同數(shù)據(jù)庫(kù)之間的交互過(guò)程自動(dòng)化了延旧。有了Core Data框架以后谋国,我們無(wú)須編寫(xiě)Objective-C代碼,又可以是使用關(guān)系型數(shù)據(jù)庫(kù)迁沫。因?yàn)镃ore Data會(huì)在底層自動(dòng)給我們生成應(yīng)該最佳優(yōu)化過(guò)的SQL語(yǔ)句芦瘾。
那么Realm是數(shù)據(jù)庫(kù)么闷盔?
Realm 不是 ORM,也不基于 SQLite 創(chuàng)建旅急,而是為移動(dòng)開(kāi)發(fā)者定制的全功能數(shù)據(jù)庫(kù)。它可以將原生對(duì)象直接映射到Realm的數(shù)據(jù)庫(kù)引擎(遠(yuǎn)不僅是一個(gè)鍵值對(duì)存儲(chǔ))中牡整。
Realm 是一個(gè) MVCC 數(shù)據(jù)庫(kù) 藐吮,底層是用 C++ 編寫(xiě)的。MVCC 指的是多版本并發(fā)控制逃贝。
Realm是滿(mǎn)足ACID的谣辞。原子性(Atomicity)、一致性(Consistency)沐扳、隔離性(Isolation)泥从、持久性(Durability)。一個(gè)支持事務(wù)(Transaction)的數(shù)據(jù)庫(kù)沪摄,必需要具有這四種特性躯嫉。Realm都已經(jīng)滿(mǎn)足。
1.Realm 采用MVCC的設(shè)計(jì)思想
MVCC 解決了一個(gè)重要的并發(fā)問(wèn)題:在所有的數(shù)據(jù)庫(kù)中都有這樣的時(shí)候杨拐,當(dāng)有人正在寫(xiě)數(shù)據(jù)庫(kù)的時(shí)候有人又想讀取數(shù)據(jù)庫(kù)了(例如祈餐,不同的線(xiàn)程可以同時(shí)讀取或者寫(xiě)入同一個(gè)數(shù)據(jù)庫(kù))。這會(huì)導(dǎo)致數(shù)據(jù)的不一致性 - 可能當(dāng)你讀取記錄的時(shí)候一個(gè)寫(xiě)操作才部分結(jié)束哄陶。
有很多的辦法可以解決讀帆阳、寫(xiě)并發(fā)的問(wèn)題,最常見(jiàn)的就是給數(shù)據(jù)庫(kù)加鎖屋吨。在之前的情況下蜒谤,我們?cè)趯?xiě)數(shù)據(jù)的時(shí)候就會(huì)加上一個(gè)鎖。在寫(xiě)操作完成之前至扰,所有的讀操作都會(huì)被阻塞鳍徽。這就是眾所周知的讀-寫(xiě)鎖。這常常都會(huì)很慢渊胸。Realm采用的是MVCC數(shù)據(jù)庫(kù)的優(yōu)點(diǎn)就展現(xiàn)出來(lái)了旬盯,速度非常快翎猛。
MVCC 在設(shè)計(jì)上采用了和 Git 一樣的源文件管理算法胖翰。你可以把 Realm 的內(nèi)部想象成一個(gè) Git,它也有分支和原子化的提交操作切厘。這意味著你可能工作在許多分支上(數(shù)據(jù)庫(kù)的版本)萨咳,但是你卻沒(méi)有一個(gè)完整的數(shù)據(jù)拷貝。Realm 和真正的 MVCC 數(shù)據(jù)庫(kù)還是有些不同的疫稿。一個(gè)像 Git 的真正的 MVCC 數(shù)據(jù)庫(kù)培他,你可以有成為版本樹(shù)上 HEAD 的多個(gè)候選者鹃两。而 Realm 在某個(gè)時(shí)刻只有一個(gè)寫(xiě)操作,而且總是操作最新的版本 - 它不可以在老的版本上工作舀凛。
Realm底層是B+樹(shù)實(shí)現(xiàn)的俊扳,在Realm團(tuán)隊(duì)開(kāi)源的realm-core里面可以看到源碼,里面有用bpTree猛遍,這是一個(gè)B+樹(shù)的實(shí)現(xiàn)馋记。B+ 樹(shù)是一種樹(shù)數(shù)據(jù)結(jié)構(gòu),是一個(gè)n叉樹(shù)懊烤,每個(gè)節(jié)點(diǎn)通常有多個(gè)孩子梯醒,一棵B+樹(shù)包含根節(jié)點(diǎn)、內(nèi)部節(jié)點(diǎn)和葉子節(jié)點(diǎn)腌紧。根節(jié)點(diǎn)可能是一個(gè)葉子節(jié)點(diǎn)茸习,也可能是一個(gè)包含兩個(gè)或兩個(gè)以上孩子節(jié)點(diǎn)的節(jié)點(diǎn)。
B+ 樹(shù)通常用于數(shù)據(jù)庫(kù)和操作系統(tǒng)的文件系統(tǒng)中壁肋。NTFS, ReiserFS, NSS, XFS, JFS, ReFS 和BFS等文件系統(tǒng)都在使用B+樹(shù)作為元數(shù)據(jù)索引号胚。B+ 樹(shù)的特點(diǎn)是能夠保持?jǐn)?shù)據(jù)穩(wěn)定有序,其插入與修改擁有較穩(wěn)定的對(duì)數(shù)時(shí)間復(fù)雜度墩划。B+ 樹(shù)元素自底向上插入涕刚。
Realm會(huì)讓每一個(gè)連接的線(xiàn)程都會(huì)有數(shù)據(jù)在一個(gè)特定時(shí)刻的快照。這也是為什么能夠在上百個(gè)線(xiàn)程中做大量的操作并同時(shí)訪問(wèn)數(shù)據(jù)庫(kù)乙帮,卻不會(huì)發(fā)生崩潰的原因杜漠。
上圖很好的展現(xiàn)了Realm的一次寫(xiě)操作流程。這里分3個(gè)階段察净,階段一中驾茴,V1指向根節(jié)點(diǎn)R。在階段二中氢卡,準(zhǔn)備寫(xiě)入操作锈至,這個(gè)時(shí)候會(huì)有一個(gè)V2節(jié)點(diǎn),指向新的R'译秦,并且新建一個(gè)分支出來(lái)峡捡,A'和C'。相應(yīng)的右孩子指向原來(lái)V1指向的R的右孩子筑悴。如果寫(xiě)入操作失敗们拙,就丟棄左邊這個(gè)分支。這樣的設(shè)計(jì)可以保證即使失敗阁吝,也僅僅只丟失最新數(shù)據(jù)砚婆,而不會(huì)破壞整個(gè)數(shù)據(jù)庫(kù)。如果寫(xiě)入成功突勇,那么把原來(lái)的R装盯,A坷虑,C節(jié)點(diǎn)放入Garbage中,于是就到了第三階段埂奈,寫(xiě)入成功迄损,變成了V2指向根節(jié)點(diǎn)。
在這個(gè)寫(xiě)入的過(guò)程中账磺,第二階段是最關(guān)鍵的海蔽,寫(xiě)入操作并不會(huì)改變?cè)袛?shù)據(jù),而是新建了一個(gè)新的分支绑谣。這樣就不用加鎖,也可以解決數(shù)據(jù)庫(kù)的并發(fā)問(wèn)題拗引。
正是B+樹(shù)的底層數(shù)據(jù)結(jié)構(gòu) + MVCC的設(shè)計(jì)借宵,保證了Realm的高性能。
2.Realm 采用了 zero-copy 架構(gòu)
因?yàn)?Realm 采用了 zero-copy 架構(gòu)矾削,這樣幾乎就沒(méi)有內(nèi)存開(kāi)銷(xiāo)壤玫。這是因?yàn)槊恳粋€(gè) Realm 對(duì)象直接通過(guò)一個(gè)本地 long 指針和底層數(shù)據(jù)庫(kù)對(duì)應(yīng),這個(gè)指針是數(shù)據(jù)庫(kù)中數(shù)據(jù)的鉤子哼凯。
通常的傳統(tǒng)的數(shù)據(jù)庫(kù)操作是這樣的欲间,數(shù)據(jù)存儲(chǔ)在磁盤(pán)的數(shù)據(jù)庫(kù)文件中,我們的查詢(xún)請(qǐng)求會(huì)轉(zhuǎn)換為一系列的SQL語(yǔ)句断部,創(chuàng)建一個(gè)數(shù)據(jù)庫(kù)連接猎贴。數(shù)據(jù)庫(kù)服務(wù)器收到請(qǐng)求,通過(guò)解析器對(duì)SQL語(yǔ)句進(jìn)行詞法和語(yǔ)法語(yǔ)義分析蝴光,然后通過(guò)查詢(xún)優(yōu)化器對(duì)SQL語(yǔ)句進(jìn)行優(yōu)化她渴,優(yōu)化完成執(zhí)行對(duì)應(yīng)的查詢(xún),讀取磁盤(pán)的數(shù)據(jù)庫(kù)文件(有索引則先讀索引)蔑祟,讀取命中查詢(xún)的每一行的數(shù)據(jù)趁耗,然后存到內(nèi)存里(這里有內(nèi)存消耗)。之后你需要把數(shù)據(jù)序列化成可在內(nèi)存里面存儲(chǔ)的格式疆虚,這意味著比特對(duì)齊苛败,這樣 CPU 才能處理它們。最后径簿,數(shù)據(jù)需要轉(zhuǎn)換成語(yǔ)言層面的類(lèi)型罢屈,然后它會(huì)以對(duì)象的形式返回,比如Objective-C的對(duì)象等牍帚。
這里就是Realm另外一個(gè)很快的原因儡遮,Realm的數(shù)據(jù)庫(kù)文件是通過(guò)memory-mapped,也就是說(shuō)數(shù)據(jù)庫(kù)文件本身是映射到內(nèi)存(實(shí)際上是虛擬內(nèi)存)中的暗赶,Realm訪問(wèn)文件偏移就好比文件已經(jīng)在內(nèi)存中一樣(這里的內(nèi)存是指虛擬內(nèi)存)鄙币,它允許文件在沒(méi)有做反序列化的情況下直接從內(nèi)存讀取肃叶,提高了讀取效率。Realm 只需要簡(jiǎn)單地計(jì)算偏移來(lái)找到文件中的數(shù)據(jù)十嘿,然后從原始訪問(wèn)點(diǎn)返回?cái)?shù)據(jù)結(jié)構(gòu)的值 因惭。
正是Realm采用了 zero-copy 架構(gòu),幾乎沒(méi)有內(nèi)存開(kāi)銷(xiāo)绩衷,Realm核心文件格式基于memory-mapped蹦魔,節(jié)約了大量的序列化和反序列化的開(kāi)銷(xiāo),導(dǎo)致了Realm獲取對(duì)象的速度特別高效咳燕。
- Realm 對(duì)象在不同的線(xiàn)程間不能共享
Realm 對(duì)象不能在線(xiàn)程間傳遞的原因就是為了保證隔離性和數(shù)據(jù)一致性勿决。這樣做的目的只有一個(gè),為了速度招盲。
由于Realm是基于零拷貝的低缩,所有對(duì)象都在內(nèi)存里,所以會(huì)自動(dòng)更新曹货。如果允許Realm對(duì)象在線(xiàn)程間共享咆繁,Realm 會(huì)無(wú)法確保數(shù)據(jù)的一致性,因?yàn)椴煌木€(xiàn)程會(huì)在不確定的什么時(shí)間點(diǎn)同時(shí)改變對(duì)象的數(shù)據(jù)顶籽。
要想保證多線(xiàn)程能共享對(duì)象就是加鎖玩般,但是加鎖又會(huì)導(dǎo)致一個(gè)長(zhǎng)時(shí)間的后臺(tái)寫(xiě)事務(wù)會(huì)阻塞 UI 的讀事務(wù)。不加鎖就不能保證數(shù)據(jù)的一致性礼饱,但是可以滿(mǎn)足速度的要求坏为。Realm在衡量之后,還是為了速度镊绪,做出了不允許線(xiàn)程間共享的妥協(xié)久脯。
正是因?yàn)椴辉试S對(duì)象在不同的線(xiàn)程間共享,保證了數(shù)據(jù)的一致性镰吆,不加線(xiàn)程鎖帘撰,保證了Realm的在速度上遙遙領(lǐng)先。 - 真正的懶加載
大多數(shù)數(shù)據(jù)庫(kù)趨向于在水平層級(jí)存儲(chǔ)數(shù)據(jù)万皿,這也就是為什么你從 SQLite 讀取一個(gè)屬性的時(shí)候摧找,你就必須要加載整行的數(shù)據(jù)。它在文件中是連續(xù)存儲(chǔ)的牢硅。
不同的是蹬耘,Realm盡可能讓 Realm 在垂直層級(jí)連續(xù)存儲(chǔ)屬性,你也可以看作是按列存儲(chǔ)减余。
在查詢(xún)到一組數(shù)據(jù)后综苔,只有當(dāng)你真正訪問(wèn)對(duì)象的時(shí)候才真正加載進(jìn)來(lái)。 - Realm 中的文件
先來(lái)說(shuō)說(shuō)中間的Database File
.realm 文件是memory mapped的,所有的對(duì)象都是文件首地址偏移量的一個(gè)引用如筛。對(duì)象的存儲(chǔ)不一定是連續(xù)的堡牡,但是Array可以保證是連續(xù)存儲(chǔ)。
.realm執(zhí)行寫(xiě)操作的時(shí)候杨刨,有3個(gè)指針晤柄,一個(gè)是current top pointer ,一個(gè)是 other top pointer 妖胀,最后一個(gè)是 switch bit芥颈。
switch bit* 標(biāo)示著top pointer是否已經(jīng)被使用過(guò)。如果被使用過(guò)了赚抡,代表著數(shù)據(jù)庫(kù)已經(jīng)是可讀的爬坑。
the top pointer優(yōu)先更新,緊接著是the switch bit更新涂臣。因?yàn)榧词箤?xiě)入失敗了妇垢,雖然丟失了所有數(shù)據(jù),但是這樣能保證數(shù)據(jù)庫(kù)依舊是可讀的肉康。
再來(lái)說(shuō)說(shuō) .lock file。
.lock文件中會(huì)包含 the shared group 的metadata灼舍。這個(gè)文件承擔(dān)著允許多線(xiàn)程訪問(wèn)相同的Realm對(duì)象的職責(zé)吼和。
最后說(shuō)說(shuō)Commit logs history
這個(gè)文件會(huì)用來(lái)更新索引indexes,會(huì)用來(lái)同步骑素。里面主要維護(hù)了3個(gè)小文件炫乓,2個(gè)是數(shù)據(jù)相關(guān)的,1個(gè)是操作management的献丑。
總結(jié)
經(jīng)過(guò)上面的分析之后末捣,深深的感受到Realm就是為速度而生的!在保證了ACID的要求下创橄,很多設(shè)計(jì)都是以速度為主箩做。當(dāng)然,Realm 最核心的理念就是對(duì)象驅(qū)動(dòng)妥畏,這是 Realm 的核心原則邦邦。Realm 本質(zhì)上是一個(gè)嵌入式數(shù)據(jù)庫(kù),但是它也是看待數(shù)據(jù)的另一種方式醉蚁。它用另一種角度來(lái)重新看待移動(dòng)應(yīng)用中的模型和業(yè)務(wù)邏輯燃辖。
Realm還是跨平臺(tái)的,多個(gè)平臺(tái)都使用相同的數(shù)據(jù)庫(kù)网棍,是多么好的一件事情呀黔龟。相信使用Realm作為App數(shù)據(jù)庫(kù)的開(kāi)發(fā)者會(huì)越來(lái)越多。