[TOC]
- Realm,為移動(dòng)設(shè)備而生!替代 SQLite 和 Core Data。為你省下數(shù)周的時(shí)間和數(shù)千行的代碼,幫你創(chuàng)造出更棒的用戶體驗(yàn)。--Realm官網(wǎng)
一.優(yōu)勢
Realm 并不是基于 Core Data ,也不是基于 SQLite 所構(gòu)建的。它擁有自己的數(shù)據(jù)庫存儲(chǔ)引擎,可以高效且快速地完成數(shù)據(jù)庫的構(gòu)建操作。
Realm 比使用 SQLite 要快,比ORM要快很多。
簡單。通過標(biāo)注和對(duì)象操作實(shí)現(xiàn)數(shù)據(jù)操作。
版本升級(jí)時(shí),數(shù)據(jù)遷移成本很低。
與rxjava、retrofit等Library有很好的交互。
二.安裝方式
- cocoapods(推薦):
1.[安裝CocoaPods 0.39.0 或者更高版本]
2.運(yùn)行 pod repo update,以確保 CocoaPods 能夠獲取到Realm 的最新版本
3.在您的Podfile中,添加pod 'Realm'到您的 app 目標(biāo)中,添加pod 'Realm/Headers'到您的測試目標(biāo)中;
4.在終端運(yùn)行pod install;
5.采用 CocoaPods 生成的.xcworkspace來運(yùn)行工程!
6.如果需要在 Swift 當(dāng)中使用的話,將于 Swift/RLMSupport.swift 的這個(gè)文件拖動(dòng)到您 Xcode 項(xiàng)目的文件導(dǎo)航器當(dāng)中,檢查以確保 **Copy items if needed** 選項(xiàng)已被勾選。
- Static Framework(靜態(tài)庫):
1.下載Realm 的最新版本并解壓;
2.將 Realm.framework 從 ios/static/文件夾拖曳到您 Xcode 項(xiàng)目中的文件導(dǎo)航器當(dāng)中。確保 **Copy items if needed** 選中然后單擊 **Finish**;
3.在 Xcode 文件導(dǎo)航器中選擇您的項(xiàng)目,然后選擇您的應(yīng)用目標(biāo),進(jìn)入到** Build Phases** 選項(xiàng)卡中。在 **Link Binary with Libraries** 中單擊 + 號(hào)然后添加 **libc++.tbd** 以及 **libz.tbd**;
4.如果你在用 Swift 來使用 Realm,那么將位于 Swift/RLMSupport.swift
的文件拖曳進(jìn)您 Xcode 項(xiàng)目中的文件導(dǎo)航器當(dāng)中,確保 **Copy items if needed** 選中。
三.Realm瀏覽器/數(shù)據(jù)庫管理器
在mac的Appstore下載一款名為Realm Browser的軟件即可進(jìn)行管理
四.Xcode插件
- 快速創(chuàng)建RLMObject對(duì)象
點(diǎn)擊下載release zip ,解壓以后打開plugin/RealmPlugin.xcodeproj進(jìn)行編譯,重啟Xcode,command + N,拉倒底部,出現(xiàn)一個(gè)Realm Model Object的圖標(biāo),點(diǎn)擊即可創(chuàng)建RLMObject對(duì)象
五.API 參考
所有的類和方法什么的都可以去API文檔查閱
六.創(chuàng)建數(shù)據(jù)模型
- 創(chuàng)建一個(gè)數(shù)據(jù)模型,并創(chuàng)建參數(shù)
#import <Realm/Realm.h>
#import "ExpandCell_M.h"
typedef enum : NSUInteger {
TransactionDetailButtonTypeAll = 0,
TransactionDetailButtonTypeRecharge,
TransactionDetailButtonTypeDeposit,
TransactionDetailButtonTypeEarnings,
} TransactionDetailButtonType;
@interface ExpandSection_M : RLMObject
/// 是否隱藏
@property (nonatomic,assign) BOOL isExpand;
/// 時(shí)間標(biāo)題
@property (nonatomic, copy) NSString *month;
///判斷類型(TransactionDetailButtonType類型轉(zhuǎn)化為NSInteger類型)
@property (nonatomic, assign) NSInteger DetailType;
///編碼
@property (nonatomic, assign) NSInteger SectionID;
@end
注意事項(xiàng):
1.Realm支持以下的屬性property種類: BOOL,bool, int, NSInteger, long, float, double, CGFloat, NSString, NSDate 和NSData。
tip: 枚舉及結(jié)構(gòu)體無法進(jìn)行存儲(chǔ),需要進(jìn)行數(shù)據(jù)類型轉(zhuǎn)換
2.你可以使用RLMArray<NSObject> 和RLMObject來模擬對(duì)一或?qū)Χ嗟年P(guān)系
3.Realm也支持RLMObject繼承。
4.屬性property特性:請注意Realm忽略了objective-c的property attributes,像nonatomic, atomic, strong, copy, weak等等。
所以,為了避免誤解,我們推薦你在寫入模型的時(shí)候不要使用任何的property attributes。但是,假如你設(shè)置了,這些attributes會(huì)一直生效直到RLMObject被寫入realm數(shù)據(jù)庫。
無論RLMObject在或不在realm中,你為getter和setter自定義的名字都能正常工作。
七.數(shù)據(jù)模型定制
幾個(gè)存在的類方法進(jìn)一步指定模型信息:
+ (NSDictionary *)defaultPropertyValues; 可以被重寫,用以為新建的對(duì)象提供默認(rèn)值。
+ (NSString *)primaryKey; 可以被重寫來設(shè)置模型的主鍵。定義主鍵可以提高效率并且確保唯一性。
+ (NSArray *)ignoredProperties; 可以被重寫來防止Realm存儲(chǔ)模型屬性。
八.模型嵌套
// .h (官網(wǎng)示例)
#import <Realm/Realm.h>
@class Person;
// 狗狗的數(shù)據(jù)模型
@interface Dog : RLMObject
@property NSString *name;
@property Person *owner;
@end
RLM_ARRAY_TYPE(Dog) // 定義RLMArray<Dog>
// 狗狗主人的數(shù)據(jù)模型
@interface Person : RLMObject
@property NSString *name;
@property NSDate *birthdate;
// 通過RLMArray建立關(guān)系
@property RLMArray<Dog> *dogs;
@end
RLM_ARRAY_TYPE(Person) // 定義RLMArray<Person>
// .m
@implementation Dog
@end // 暫無使用
@implementation Person
@end // 暫無使用
八.使用Realm進(jìn)行數(shù)據(jù)管理
- 方法一:
RLMRealm *realm = [RLMRealm defaultRealm];
[realm transactionWithBlock:^{
// 進(jìn)行數(shù)據(jù)處理
}];
- 方法二:
RLMRealm *realm = [RLMRealm defaultRealm];
[realm beginWriteTransaction];
// 進(jìn)行數(shù)據(jù)處理
[realm commitWriteTransaction];
九.查詢
1.所有的數(shù)據(jù)抓取都很簡單,并且直到獲得數(shù)據(jù)之后才創(chuàng)建副本。
關(guān)于使用RLMResults的小貼士:
Realm的對(duì)象查詢返回一個(gè)RLMResults對(duì)象。它包含了一系列的RLMObject。(保存什么類型,取出就是什么類型)
RLMResults有一個(gè)與NSArray很相似的interface(接口)并且對(duì)象可以通過索引(index)下標(biāo)獲取。
但不同于NSArray的是,RLMResult是歸類的——它只能容納一種RLMObjects類型。(數(shù)組類型單一)根據(jù)種類獲取對(duì)象從realm中獲取對(duì)象的最基本方法就是 [RLMObject allObjects], 它返回一個(gè)RLMResults,里面是查詢的子類的所有RLMObject實(shí)例。
// 默認(rèn)查詢
RealmRLMResults *dogs = [Dog allObjects]; // 從默認(rèn)Realm中查找所有的??
// 指定查詢
RealmRLMRealm *petsRealm = [RLMRealm realmWithPath:@"pets.realm"]; // 得到一個(gè)指定的realm
RealmRLMResults *otherDogs = [Dog allObjectsInRealm:petsRealm]; // 在指定的realm中查詢所有的狗
2.謂詞/條件查詢
如果你對(duì)NSPredicate很熟悉的話, 那么你就已經(jīng)知道怎么在realm里面查詢了。
RLMObjects, RLMRealm, RLMArray和RLMResults都提供很好的methods來查詢特定的RLMObjects:
你只需要傳遞相應(yīng)地NSPredicate實(shí)例,謂詞字符串,謂詞格式字符串,就可以獲取你想要的RLMObjects實(shí)例。就和NSObject一樣的。
舉個(gè)例子,下面的代碼就是對(duì)上面的拓展。 通過調(diào)用[RLMObject objectsWhere:],獲得了默認(rèn)realm數(shù)據(jù)庫中的所有顏色是黃褐色的,名字開頭是“B”的狗的實(shí)例。
// 條件查詢
RLMResults *tanDogs = [Dog objectsWhere:@"color = 'tan' AND name BEGINSWITH 'B'"];
// 使用一個(gè)NSPredicate對(duì)象查詢
NSPredicate *pred = [NSPredicate predicateWithFormat:@"color = %@ AND name BEGINSWITH %@", @"tan", @"B"];
tanDogs = [Dog objectsWithPredicate:pred];
可以參看Apple的Predicates Programming Guide
了解更多關(guān)于如何創(chuàng)建謂詞。
1.Realm支持很多常見的謂詞:在比較中, 操作數(shù)可以是屬性名或者常量。但是其中至少有一個(gè)是屬性名。
2.只有int, long, float, double, and NSDate這些屬性類型(property types)支持 ==, <=, <, >=, >, !=, 和 BETWEEN這些比較操作符。布爾屬性可以支持==和!=。
3.在NSString和NSData屬性中, 我們支持的操作符有 ==, !=, BEGINSWITH, CONTAINS和ENDSWITH。
4.realm還支持如下的復(fù)合型操作符: AND, OR, NOT注意,我們雖然不支持aggregate expression type,但是我們支持BETWEEN操作符, 例如:
RLMResults *results = [Person objectsWhere:@"age BETWEEN %@", @[42, 43]];
3.條件排序
1.在很多情況下,我們都希望獲取或者查詢返回的結(jié)果都能按照一定條件排序。
所以,RLMArray支持使用指定的屬性對(duì)數(shù)據(jù)列進(jìn)行排序。
2.Realm允許你指定一個(gè)排序要求并且根據(jù)一個(gè)或多個(gè)屬性進(jìn)行排序。
3.舉例來說, 下面代碼呼叫了[RLMObject objectsWhere:where:]對(duì)返回的數(shù)據(jù)”dogs”進(jìn)行排序,排序的條件是名字的字母表升序。:
// Sort tan dogs with names starting with "B" by name
RLMResults *sortedDogs = [[Dog objectsWhere:
@"color = 'tan' AND name BEGINSWITH 'B'"] sortedResultsUsingProperty:@"name" ascending:YES];
4.鏈?zhǔn)讲樵?/h4>
- Realm查詢引擎的一個(gè)獨(dú)特屬性就是它能夠進(jìn)行簡單快捷的鏈?zhǔn)讲樵儯?而不需要像傳統(tǒng)數(shù)據(jù)庫一樣的麻煩。舉個(gè)例子來說,假如你要所有黃褐色的小狗的結(jié)果序列,然后從中查找名字開頭是“B“的小狗。 你可以發(fā)送如下的請求。
RLMResults *tanDogs = [Dog objectsWhere:@"color = 'tan'"];
RLMResults *tanDogsWithBNames = [tanDogs objectsWhere:@"name BEGINSWITH 'B'"];
5.刪除數(shù)據(jù)
- 刪除某個(gè)在Realm數(shù)據(jù)庫中的數(shù)據(jù)。
Book *cheeseBook = ... // 存儲(chǔ)在 Realm 中的 Book 對(duì)象
// 在事務(wù)中刪除一個(gè)對(duì)象
[realm beginWriteTransaction];
[realm deleteObject:cheeseBook];
[realm commitWriteTransaction];
- 刪除數(shù)據(jù)庫中的所有數(shù)據(jù)。
// 從 Realm 中刪除所有數(shù)據(jù)
[realm beginWriteTransaction];
[realm deleteAllObjects];
[realm commitWriteTransaction];
十.通知
RLMResults *tanDogs = [Dog objectsWhere:@"color = 'tan'"];
RLMResults *tanDogsWithBNames = [tanDogs objectsWhere:@"name BEGINSWITH 'B'"];
Book *cheeseBook = ... // 存儲(chǔ)在 Realm 中的 Book 對(duì)象
// 在事務(wù)中刪除一個(gè)對(duì)象
[realm beginWriteTransaction];
[realm deleteObject:cheeseBook];
[realm commitWriteTransaction];
// 從 Realm 中刪除所有數(shù)據(jù)
[realm beginWriteTransaction];
[realm deleteAllObjects];
[realm commitWriteTransaction];
每當(dāng)一次寫事務(wù)完成Realm實(shí)例都會(huì)向其他線程上的實(shí)例發(fā)出通知,可以通過注冊一個(gè)block來響應(yīng)通知:
// Observe Realm Notifications
self.token = [realm addNotificationBlock:^(NSString *note, RLMRealm * realm) {
[myViewController updateUI];
}];
只要有任何的引用指向這個(gè)返回的notification token,它就會(huì)保持激活狀態(tài)。
在這個(gè)注冊更新的類里,你需要有一個(gè)強(qiáng)引用來鉗制這個(gè)token, 因?yàn)橐坏﹏otification token被釋放,通知也會(huì)自動(dòng)解除注冊。
具體內(nèi)容:
[Realm addNotificationBlock:]和[Realm removeNotificationBlock:]
。
十一.Realm配置
每個(gè)用戶有自己不同的數(shù)據(jù)庫,在App啟動(dòng)以后根據(jù)用戶的uid來設(shè)置數(shù)據(jù)庫,可以通過對(duì)默認(rèn)配置進(jìn)行更改,然后通過訪問默認(rèn)數(shù)據(jù)庫來實(shí)現(xiàn)不同用戶不同數(shù)據(jù)庫.
因?yàn)樵O(shè)置了模型插入數(shù)據(jù)庫以后如果發(fā)生屬性更改,需要進(jìn)行版本遷移.可以使用app的版本作為數(shù)據(jù)庫的版本,當(dāng)版本迭代發(fā)生以后,改了模型的屬性,通過更改App的版本號(hào)實(shí)現(xiàn)版本遷移.
// 版本遷移和配置數(shù)據(jù)庫基本數(shù)據(jù)
- (void)setRealmMigration:(NSString *)username{
RLMRealmConfiguration *config = [RLMRealmConfiguration defaultConfiguration];
// BTLog(@"%@---",config.fileURL);
// 使用默認(rèn)的目錄,但是使用用戶名來替換默認(rèn)的文件名
config.fileURL = [[[config.fileURL URLByDeletingLastPathComponent]
URLByAppendingPathComponent:username ? username : @"defalut"]
URLByAppendingPathExtension:@"realm"];
// 將這個(gè)配置應(yīng)用到默認(rèn)的 Realm 數(shù)據(jù)庫當(dāng)中
[RLMRealmConfiguration setDefaultConfiguration:config];
// 設(shè)置新的架構(gòu)版本。這個(gè)版本號(hào)必須高于之前所用的版本號(hào)(如果您之前從未設(shè)置過架構(gòu)版本,那么這個(gè)版本號(hào)設(shè)置為 0)
NSDictionary *infoDic = [[NSBundle mainBundle] infoDictionary];
NSString *appVersion = [infoDic objectForKey:@"CFBundleShortVersionString"];
uint64_t schemaVersion = appVersion.floatValue;
config.schemaVersion = schemaVersion;
// 設(shè)置閉包,這個(gè)閉包將會(huì)在打開低于上面所設(shè)置版本號(hào)的 Realm 數(shù)據(jù)庫的時(shí)候被自動(dòng)調(diào)用
config.migrationBlock = ^(RLMMigration *migration, uint64_t oldSchemaVersion) {
// 目前我們還未進(jìn)行數(shù)據(jù)遷移,因此 oldSchemaVersion == 0
if (oldSchemaVersion < schemaVersion) {
// 什么都不要做!Realm 會(huì)自行檢測新增和需要移除的屬性,然后自動(dòng)更新硬盤上的數(shù)據(jù)庫架構(gòu)
}
};
// 告訴 Realm 為默認(rèn)的 Realm 數(shù)據(jù)庫使用這個(gè)新的配置對(duì)象
[RLMRealmConfiguration setDefaultConfiguration:config];
// 現(xiàn)在我們已經(jīng)告訴了 Realm 如何處理架構(gòu)的變化,打開文件之后將會(huì)自動(dòng)執(zhí)行遷移
[RLMRealm defaultRealm];
// realm文件的位置
BTLog(@"fileurl===%@",[RLMRealmConfiguration defaultConfiguration].fileURL);
}