Realm数据库存储 使用详解
文章目錄
一 Realm 框架
- 概念介紹
- 開發輔助工具
二 Realm 使用教程
- 1 簡單的數據操作
- 創建數據模型
- 使用RLMRealm對象保存指定模型
- 使用RLMRealm對象 更新指定模型
- 使用RLMRealm對象 刪除數據
- 查詢數據
- 2 支持的數據類型
- 3 模型與模型之間的關系
- 對一關系
- 對多關系
- 反向關系
- 4 可空屬性&默認值&忽略屬性
- 5 通知
- 6 Realm數據庫
- 用戶機制
- 只讀方式打開數據庫
- 數據庫文件刪除
- 7 數據庫遷移
- 數據結構的遷移
- 數據遷移
- 屬性重命名
- 多版本增量式遷移
一 Realm 框架
概念介紹:
1 Realm 本質上是一個嵌入式數據庫,但是它也是看待數據的另一種方式。它用另一種角度來重新看待你移動應用中的模型和業務邏輯。我們所做的就是嘗試減少數據庫讀寫的開銷。我們盡可能讓其運行得足夠快,因此我們一直在調整性能數據。
2 Realm核心數據庫引擎打造的,并不是建立在SQLite之上的ORM(對象-關系映射(OBJECT/RELATIONALMAPPING,簡稱ORM)) 是擁有獨立的數據庫存儲引擎
3 性能: 比SQLite和coreData牛逼?(ps:FMDB和coreData 都是基于SQLite封裝的)
4 易用 相對于SQLite 和coreData 使用更加簡單 更易入門
輔助工具
1 在App Store 搜索 Realm Browser(可視化訪問Realm數據庫)
2 Xcode 插件 https://github.com/alcatraz/Alcatraz (下載下來后直接編譯就會發現xcode中會多了個這個 如下圖)
二 realm使用教程:
使用cocoapod集成
pod 'Realm' 復制代碼1 簡單的數據操作
######(1) 創建數據模型 繼承自RLMObject 也就是上圖中的類
創建的模型屬性不需要任何屬性修飾符 這是因為 由于Realm 在自己的引擎內部有很好的語義解釋系統,所以 Objective?C 的許多屬性特性將被忽略,如nonatomic, atomic, strong, copy 和 weak 等。 因此為了避免誤解,官方推薦在編寫數據模型的時候不要使用任何的屬性特性。 請注意,所有的必需屬性都必須在對象添加到 Realm 前被賦值
創建對象的方式 // 后邊的值 可以出 字典 也可以傳數組// 創建對象 字典testOneModel *model = [[testOneModel alloc]initWithValue:@{@"num":@2,@"name":@"奧卡姆剃須刀"}];// 數組 后邊保存的值 要和模型中的屬性 保持一致并且順序也一致 一一對應testOneModel *model = [[testOneModel alloc]initWithValue:@[@2,@"奧卡姆剃須刀"]];復制代碼######(2) 使用RLMRealm對象保存指定模型
// 1 獲取RLMRealm對象 可以看做是操作數據庫的句柄 操作數據庫全靠它RLMRealm *realm = [RLMRealm defaultRealm];// 寫入方法一// 開始寫入事物[realm beginWriteTransaction];// 保存數據庫[realm addObject:model];// 提交寫入事物[realm commitWriteTransaction];// 方法二 簡潔// 提供的有block 省去創建Model[realm transactionWithBlock:^{[realm addObject:model];}];// 方法三 省去了創建Model[realm transactionWithBlock:^{[testOneModel createInRealm:realm withValue:@{@"num":@2,@"name":@"奧卡姆剃須刀"}];}];復制代碼######(3) 使用RLMRealm對象 更新指定模型
// 方式一 // 然后在事物中直接更改[realm transactionWithBlock:^{model.num = 27;}];// 方式二 直接從realm中取到模型來更新RLMResults *results = [testOneModel objectsWhere:@"num = 27"];testOneModel *model = results.firstObject;[realm transactionWithBlock:^{model.num = 30;}];// 方式三 根據主鍵進行更新 但是前提是必須在模型中設置好主鍵 然后此方法會判斷該主鍵是否存在 如果存在就更改 不存在就添加[realm transactionWithBlock:^{[realm addOrUpdateObject:model];}];// 方式4 和之前的就差不多了 直接在事物中創建模型[realm transactionWithBlock:^{[testOneModel createOrUpdateInRealm:realm withValue:@[@10,@"奧卡刀"]];}];復制代碼方式三的配圖
設置完成后數據庫中該屬性也變成主鍵######(4) 使用RLMRealm對象 刪除數據
// 1 刪除指定的對象// 刪除的模型一定要求是被realm所管理的 也就是改模型一定是要從realm中取出來的 RLMResults *results = [testOneModel objectsWhere:@"num = 27"];testOneModel *model = results.firstObject;[realm transactionWithBlock:^{[realm deleteObject:model];}];// 2 刪除指定模型里邊的所有數據 通過遍歷 只會刪除該模型RLMResults *modelRes = [testOneModel allObjects];for (testOneModel *model in modelRes) {[realm transactionWithBlock:^{[realm deleteObject:model];}];}// 3 刪除所有的數據 所有的模型數據都會被清空[realm transactionWithBlock:^{ // 刪除所有的模型[realm deleteAllObjects];}]; // 4 根據主鍵查詢到改模型來刪除testOneModel *testModel = [testOneModel objectInRealm:realm forPrimaryKey:@27];[realm transactionWithBlock:^{[realm deleteObject:testModel];}];復制代碼######(5) 查詢數據
// 注意事項: 所有的查詢(包括查詢和屬性訪問)在Realm中都是懶加載的 只有當屬性被訪問時 才能夠讀取相應的數據 // 查詢結棍并不是數據的拷貝(只是數據的映射) 修改查詢結果(在寫入事務中)會直接修改硬盤上的數據。//一旦檢索執行之后, RLMResults 將隨時保持更新// 1 查詢所有數據RLMResults *modelRes = [testOneModel allObjects];// 2 條件查詢RLMResults *results = [testOneModel objectsWhere:@"num = 27"];// 3 查詢結果排序RLMResults *modelRes = [testOneModel allObjects]; // 排序RLMResults *sortRes = [modelRes sortedResultsUsingKeyPath:@"num" ascending:YES];// 4 鏈式查詢 // (1) 得到一個查詢結果RLMResults *modelRes2 = [testOneModel objectsWhere:@"num > 1"]; // (2) 在該結果集上繼續進行查詢 得到一個新的結果集RLMResults *modelRes3 = [modelRes objectsWhere:@"num < 10"];// 5 分頁 (這個分頁我們客戶端做的比較少 一般都是后臺做處理 ) // 查詢出來的結果對象是懶加載 只有真正訪問時 才會加載相應對象 所以這里的分頁 其實就是從所有集合中分頁獲取即可RLMResults *allModels = [testOneModel allObjects];for (int i = 3; i <= 6; i++) {testOneModel *model = allModels[i];}復制代碼2 支持的數據類型
支持的類型: BOOL, bool, int, NSInteger, long, long long, float, double, NSString, NSDate, NSData, and NSNumber // 不支持集合類型 // 解決方案 1 序列化成NSData進行存儲 2 轉換成RLMArray<RLMObject>進行存儲 // 例如存儲ImagetestOneModel *model = [[testOneModel alloc]init];model.num = 10;model.name = @"奧卡姆剃須刀";// realm不支持image 不能這樣存錯 // model.image = [UIImage imageNamed:@"123.jpg"];正確的存儲方式NSString *filePath = [[NSBundle mainBundle] pathForResource:@"123.jpg" ofType:nil];NSData *data = [NSData dataWithContentsOfFile:filePath];model.imageData = data;RLMRealm *realm = [RLMRealm defaultRealm];[realm transactionWithBlock:^{[realm addOrUpdateObject:model];}];復制代碼但是需要忽略到模型中的image 采用readOnly修飾即可 然后重寫image的get方法 可以在外界取到改圖片
- (UIImage *)image{ return [UIImage imageWithData:self.imageData]; }
包括集合類的也不能直接存儲
存儲集合 我們需要在創建一個繼承自RLMObject的模型@interface testTwoModel : RLMObject@property NSString *videoName;@end// 這個testTwoModel 是 專門轉換數組的 返回來的字符串類邊先轉換成該模型// 這個集合有個要求 里邊存儲的屬性必須是繼承自RLMObject類型的 @property RLMArray<testTwoModel *><testTwoModel> *arrays;復制代碼3 模型與模型之間的關系
######(1) 對一關系 當一個對象持有另外一個對象時, 比如人有一輛車
@interface Person : RLMObject @property int num; @property NSString *name; // 對一關系 @property Car *car; 復制代碼######(2) 對多關系 當一個對象持有另外一個對象時, 人有車 但是 不是只有一輛 是有很多輛車 是一個集合
有兩個規則是要遵守的
2 在Person中, 定義屬性
@property (nonatomic, strong) RLMArray<Dog *><Dog> *dogs; 復制代碼注意 雖然可以給 RLMArray 屬性賦值為 nil,但是這僅用于“清空”數組,而不是用以移除數組。這意味著您總是可以向一個 RLMArray 屬性中添加對象,即使其被置為了 nil。
Person *p = [[Person alloc]init];p.num = 10;p.name = @"剃須刀"; Car *car1 = [Car new];car1.name = @"Smart"; Car *car2 = [Car new];car2.name = @"BMW";// 初始化的集合為nil 但是其內部已經幫我們做好了 我們可以這樣直接添加[p.cars addObject:car1];[p.cars addObject:car2];RLMRealm *realm = [RLMRealm defaultRealm];[realm transactionWithBlock:^{ [realm addObject:p];}];復制代碼######(3) 反向關系 人擁有汽車 汽車又擁有人
1 在car.h中定義屬性@property (readonly)RLMLinkingObjects *master;2 在car.m 中實現協議方法 表明鏈接關系 即可 + (NSDictionary<NSString *,RLMPropertyDescriptor *> *)linkingObjectsProperties{return @{@"master":[RLMPropertyDescriptor descriptorWithClass:NSClassFromString(@"Person") propertyName:@"cars"]}; } 外界不需要單獨給car的屬性master復制 就可以得到master 如下Person *p = [Person allObjects].firstObject;NSLog(@"%@",p.cars.firstObject.master); 復制代碼4 可空屬性&默認值&忽略屬性
######(1) 默認情況下 屬性值可以為空 如果強制要求每個屬性非空,可以使用如下方法
//在.m 中實現這個方法 如果在此復制為nil 則會拋出異常 // 限制此屬性不能為空 + (NSArray<NSString *> *)requiredProperties{return @[@"name"]; }復制代碼######(2)給屬性設置默認值
// 設置指定屬性的默認值 + (NSDictionary *)defaultPropertyValues{return @{@"name":@"黑娃"}; } 復制代碼######(3)忽略屬性不存儲
//1 使用readonly修飾 就不會存儲改屬性 @property (readonly)NSString *time;// 2 也可以實現下邊的這個方法 也不會存儲該數組中的屬性 + (NSArray *)ignoredProperties { return @[@"time"]; }復制代碼可以借助這幾個 忽略屬性 和只讀屬性 打造計算屬性 完成集合 以及UIImage 對象的存儲
5 通知
一般用于 realm 存儲數據成功后 發送通知 刷新列表
// 必須要將token有強引用 @property(nonatomic, strong)RLMNotificationToken *token;// 初始化 - (void)setUp {[super setUp];RLMRealm *realm = [RLMRealm defaultRealm];// 1 第一種方式添加通知 必須要將token有強引用self.token = [realm addNotificationBlock:^(RLMNotification _Nonnull notification, RLMRealm * _Nonnull realm) {// 接收到通知后 可做刷新}]; }// 第二種方式添加通知 // 可以具體拿到更改了什么數據self.token2 = [results addNotificationBlock:^(RLMResults * _Nullable results, RLMCollectionChange * _Nullable change, NSError * _Nullable error) {NSLog(@"%@---%@---%@",results,change,error);}];// 2 存儲成功后 會默認調用通知的方法NoticeModel *model = [[NoticeModel alloc]initWithValue:@[@10]];RLMRealm *realm = [RLMRealm defaultRealm]; [realm transactionWithBlock:^{[realm addObject:model];}];// 釋放 - (void)tearDown {// 3 停止token 相當去移除通知[self.token stop];[super tearDown]; }復制代碼6 Realm數據庫
######(1)用戶機制 在我們實際開發中肯定會有很多的用戶 所以不同的用戶 。肯定要使用不同的數據庫文件
// 調用此方法 配置數據庫的名字[self setDefailtRealmForUser:@"tixvdao"];// 然后存儲的數據 都會在此數據庫中RLMRealm *realm = [RLMRealm defaultRealm];Data *data = [Data new];data.a = 10;[realm transactionWithBlock:^{[realm addObject:data];}];// 配置數據的名字 - (void)setDefaultRealmForUser:(NSString *)username {RLMRealmConfiguration *config = [RLMRealmConfiguration defaultConfiguration];// 使用默認的目錄,但是使用用戶名來替換默認的文件名config.fileURL = [[[config.fileURL URLByDeletingLastPathComponent]URLByAppendingPathComponent:username]URLByAppendingPathExtension:@"realm"];// 將這個配置應用到默認的 Realm 數據庫當中[RLMRealmConfiguration setDefaultConfiguration:config];復制代碼######(2) 只讀方式打開數據庫
RLMRealmConfiguration *config = [RLMRealmConfiguration defaultConfiguration];// 以只讀模式打開文件 因為應用數據包并不可寫config.readOnly = YES;[RLMRealmConfiguration setDefaultConfiguration:config];RLMRealm *realm = [RLMRealm defaultRealm];// 設置了只讀屬性后 就不能再添加或修改數據了 此步驟會報錯Data *data = [Data new];data.a = 12;[realm transactionWithBlock:^{[realm addObject:data];}];復制代碼######(3) 數據庫文件刪除
注意 需要刪除數據庫文件以及輔助文件
//1 設置默認數據庫的路徑[self setDefailtRealmForUser:@"tixvdao"];NSFileManager *manager = [NSFileManager defaultManager]; RLMRealmConfiguration *config = [RLMRealmConfiguration defaultConfiguration];NSArray <NSURL *>* realmFileURLs = @[config.fileURL,[config.fileURL URLByAppendingPathExtension:@"lock"],[config.fileURL URLByAppendingPathExtension:@"log_a"],[config.fileURL URLByAppendingPathExtension:@"log_b"],[config.fileURL URLByAppendingPathExtension:@"note"],];for (NSURL *url in realmFileURLs) {NSError *error = nil;[manager removeItemAtURL:url error:&error];}復制代碼7 數據庫遷移
適用于修改了數據模型的情況
######(1) 數據結構的遷移
// 正常的應該吧此數據庫遷移放在appdelegate中 程序一啟動就要做數據庫的遷移// 1 獲取默認配置RLMRealmConfiguration *config = [RLMRealmConfiguration defaultConfiguration];// 2 設置新的架構版本 這個版本高于之前的所用的版本 如果之前沒有設置版本 那版本號就是0int nerVersion = 1;config.schemaVersion = nerVersion;// 3 具體遷移 設置閉包 這個閉包將會在打開低于上邊的版本號的realm 數據庫的時候被自動調用config.migrationBlock = ^(RLMMigration * _Nonnull migration, uint64_t oldSchemaVersion) {// 目前我們還未進行數據遷移 因此oldSchemaVersion == 0if (oldSchemaVersion < nerVersion) {// 這里邊什么都不需要做 realm會自行檢測新增和需要移除的屬性 然后自動更新硬盤上的數據庫架構}};// 4 告訴realm 為默認的realm 數據庫使用這個新的配置對象[RLMRealmConfiguration setDefaultConfiguration:config];// 現在我們已經告訴 了 realm 如何處理架構的變化 打開文件之后將會自動執行遷移// 如果需要立即遷移 只要訪問數據后就會直接執行[RLMRealm defaultRealm];復制代碼######(2) 數據遷移
// 正常的應該吧此數據庫遷移放在appdelegate中 程序一啟動就要做數據庫的遷移// 1 獲取默認配置RLMRealmConfiguration *config = [RLMRealmConfiguration defaultConfiguration];// 2 設置新的架構版本 這個版本高于之前的所用的版本 如果之前沒有設置版本 那版本號就是0// 一定要記得每一次版本號疊加int nerVersion = 2;config.schemaVersion = nerVersion;// 3 具體遷移 設置閉包 這個閉包將會在打開低于上邊的版本號的realm 數據庫的時候被自動調用config.migrationBlock = ^(RLMMigration * _Nonnull migration, uint64_t oldSchemaVersion) {// 目前我們還未進行數據遷移 因此oldSchemaVersion == 0if (oldSchemaVersion < nerVersion) {// (1) 數據結構遷移的話 這里什么都不需要做// 這里邊什么都不需要做 realm會自行檢測新增和需要移除的屬性 然后自動更新硬盤上的數據庫架構// (2) 數據遷移的話 可以把原來的 值 合并到新的值// enumerateObjects:block: 方法遍歷了存儲在 Realm 文件中的每一個“Migretion”對象[migration enumerateObjects:@"Migretion" block:^(RLMObject * _Nullable oldObject, RLMObject * _Nullable newObject) {newObject[@"fullName"] = [NSString stringWithFormat:@"%@%@",oldObject[@"preName"],oldObject[@"lastName"]];}];}};// 4 告訴realm 為默認的realm 數據庫使用這個新的配置對象[RLMRealmConfiguration setDefaultConfiguration:config];// 現在我們已經告訴 了 realm 如何處理架構的變化 打開文件之后將會自動執行遷移// 如果需要立即遷移 只要訪問數據后就會直接執行[RLMRealm defaultRealm];// 1 preName和 lastName 字段都已經沒有了 將之前的數據全部合并到新的fullName字段 Migretion *model = [[Migretion alloc]init]; // // model.preName = @"aokamu"; // model.lastName = @"tixvdao";model.fullName = @"tixvdao";RLMRealm *realm = [RLMRealm defaultRealm];[realm transactionWithBlock:^{[realm addObject:model];}];復制代碼######(3) 屬性重命名
// 3 更高屬性名 這中方法性能不好 // [migration enumerateObjects:@"Migretion" block:^(RLMObject * _Nullable oldObject, RLMObject * _Nullable newObject) { // newObject[@"fullTitle"] = oldObject[@"fullName"]; // }];// 更名屬性 特有的額方法[migration renamePropertyForClass:@"Migretion" oldName:@"fullName" newName:@"fullTitle"]; 復制代碼######(4) 多版本增量式遷移
多版本增量式遷移的意思就是 版本升級 會有不同的情況 V0 -> V1 V1 -> V2 V0 -> V2
//V0 @property int age; @property NSString *preName; @property NSString *lastName;//V1 @property int age; @property NSString *fullName;// V2 @property int age; @property NSString *fullTitle;// 遷移核心代碼// 3 具體遷移 設置閉包 這個閉包將會在打開低于上邊的版本號的realm 數據庫的時候被自動調用config.migrationBlock = ^(RLMMigration * _Nonnull migration, uint64_t oldSchemaVersion) {// 目前我們還未進行數據遷移 因此oldSchemaVersion == 0if (oldSchemaVersion < 1) {// (1) 數據結構遷移的話 這里什么都不需要做// 這里邊什么都不需要做 realm會自行檢測新增和需要移除的屬性 然后自動更新硬盤上的數據庫架構}if (oldSchemaVersion < 2) {// (2) 數據遷移的話 可以把原來的 值 合并到新的值// enumerateObjects:block: 方法遍歷了存儲在 Realm 文件中的每一個“Migretion”對象 // [migration enumerateObjects:@"Migretion" block:^(RLMObject * _Nullable oldObject, RLMObject * _Nullable newObject) { // newObject[@"fullName"] = [NSString stringWithFormat:@"%@%@",oldObject[@"preName"],oldObject[@"lastName"]]; // }];}if (oldSchemaVersion < 3) { // 3 更高屬性名 這中方法性能不好 // [migration enumerateObjects:@"Migretion" block:^(RLMObject * _Nullable oldObject, RLMObject * _Nullable newObject) { // newObject[@"fullTitle"] = oldObject[@"fullName"]; // }];// 更名屬性 特有的額方法[migration renamePropertyForClass:@"Migretion" oldName:@"fullName" newName:@"fullTitle"]; } }; 復制代碼《經過多次更新終于完結...》
總結
以上是生活随笔為你收集整理的Realm数据库存储 使用详解的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【问题记录】eclipse启动web项目
- 下一篇: 直播系统搭建关键步骤与要点!