Realm踩坑筆記

[TOC]

  • Realm,為移動(dòng)設(shè)備而生!替代 SQLite 和 Core Data。為你省下數(shù)周的時(shí)間和數(shù)千行的代碼,幫你創(chuàng)造出更棒的用戶(hù)體驗(yàn)。--Realm官網(wǎng)

一.優(yōu)勢(shì)

Realm 并不是基于 Core Data ,也不是基于 SQLite 所構(gòu)建的。它擁有自己的數(shù)據(jù)庫(kù)存儲(chǔ)引擎,可以高效且快速地完成數(shù)據(jù)庫(kù)的構(gòu)建操作。

Realm 比使用 SQLite 要快,比ORM要快很多。
簡(jiǎn)單。通過(guò)標(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'到您的測(cè)試目標(biāo)中;
4.在終端運(yùn)行pod install;
5.采用 CocoaPods 生成的.xcworkspace來(lái)運(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)庫(kù)):
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 來(lái)使用 Realm,那么將位于 Swift/RLMSupport.swift
 的文件拖曳進(jìn)您 Xcode 項(xiàng)目中的文件導(dǎo)航器當(dāng)中,確保 **Copy items if needed** 選中。

三.Realm瀏覽器/數(shù)據(jù)庫(kù)管理器

在mac的Appstore下載一款名為Realm Browser的軟件即可進(jìn)行管理

四.Xcode插件

  • 快速創(chuàng)建RLMObject對(duì)象

點(diǎn)擊下載release zip ,解壓以后打開(kāi)plugin/RealmPlugin.xcodeproj進(jìn)行編譯,重啟Xcode,command + N,拉倒底部,出現(xiàn)一個(gè)Realm Model Object的圖標(biāo),點(diǎn)擊即可創(chuàng)建RLMObject對(duì)象

五.API 參考

所有的類(lèi)和方法什么的都可以去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;

///判斷類(lèi)型(TransactionDetailButtonType類(lèi)型轉(zhuǎn)化為NSInteger類(lèi)型)
@property (nonatomic, assign) NSInteger DetailType;

///編碼
@property (nonatomic, assign) NSInteger SectionID;

@end

注意事項(xiàng):

1.Realm支持以下的屬性property種類(lèi): BOOL,bool, int, NSInteger, long, float, double, CGFloat, NSString, NSDate 和NSData。
tip: 枚舉及結(jié)構(gòu)體無(wú)法進(jìn)行存儲(chǔ),需要進(jìn)行數(shù)據(jù)類(lèi)型轉(zhuǎn)換
2.你可以使用RLMArray<NSObject> 和RLMObject來(lái)模擬對(duì)一或?qū)Χ嗟年P(guān)系
3.Realm也支持RLMObject繼承。
4.屬性property特性:請(qǐng)注意Realm忽略了objective-c的property attributes,像nonatomic, atomic, strong, copy, weak等等。

所以,為了避免誤解,我們推薦你在寫(xiě)入模型的時(shí)候不要使用任何的property attributes。但是,假如你設(shè)置了,這些attributes會(huì)一直生效直到RLMObject被寫(xiě)入realm數(shù)據(jù)庫(kù)。
無(wú)論RLMObject在或不在realm中,你為getter和setter自定義的名字都能正常工作。

七.數(shù)據(jù)模型定制

幾個(gè)存在的類(lèi)方法進(jìn)一步指定模型信息:

+ (NSDictionary *)defaultPropertyValues; 可以被重寫(xiě),用以為新建的對(duì)象提供默認(rèn)值。

+ (NSString *)primaryKey; 可以被重寫(xiě)來(lái)設(shè)置模型的主鍵。定義主鍵可以提高效率并且確保唯一性。

+ (NSArray *)ignoredProperties; 可以被重寫(xiě)來(lái)防止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;

// 通過(guò)RLMArray建立關(guān)系
@property RLMArray<Dog> *dogs;

@end
RLM_ARRAY_TYPE(Person) // 定義RLMArray<Person>


// .m
@implementation Dog
@end  // 暫無(wú)使用

@implementation Person
@end  // 暫無(wú)使用

八.使用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];

九.查詢(xún)

1.所有的數(shù)據(jù)抓取都很簡(jiǎn)單,并且直到獲得數(shù)據(jù)之后才創(chuàng)建副本。

關(guān)于使用RLMResults的小貼士:
  • Realm的對(duì)象查詢(xún)返回一個(gè)RLMResults對(duì)象。它包含了一系列的RLMObject。(保存什么類(lèi)型,取出就是什么類(lèi)型)

  • RLMResults有一個(gè)與NSArray很相似的interface(接口)并且對(duì)象可以通過(guò)索引(index)下標(biāo)獲取。
    但不同于NSArray的是,RLMResult是歸類(lèi)的——它只能容納一種RLMObjects類(lèi)型。(數(shù)組類(lèi)型單一)

  • 根據(jù)種類(lèi)獲取對(duì)象從realm中獲取對(duì)象的最基本方法就是 [RLMObject allObjects], 它返回一個(gè)RLMResults,里面是查詢(xún)的子類(lèi)的所有RLMObject實(shí)例。

// 默認(rèn)查詢(xún) 
RealmRLMResults *dogs = [Dog allObjects]; // 從默認(rèn)Realm中查找所有的??

// 指定查詢(xún)
RealmRLMRealm *petsRealm = [RLMRealm realmWithPath:@"pets.realm"]; // 得到一個(gè)指定的realm
RealmRLMResults *otherDogs = [Dog allObjectsInRealm:petsRealm]; // 在指定的realm中查詢(xún)所有的狗

2.謂詞/條件查詢(xún)

如果你對(duì)NSPredicate很熟悉的話, 那么你就已經(jīng)知道怎么在realm里面查詢(xún)了。
RLMObjects, RLMRealm, RLMArray和RLMResults都提供很好的methods來(lái)查詢(xún)特定的RLMObjects:
你只需要傳遞相應(yīng)地NSPredicate實(shí)例,謂詞字符串,謂詞格式字符串,就可以獲取你想要的RLMObjects實(shí)例。就和NSObject一樣的。

舉個(gè)例子,下面的代碼就是對(duì)上面的拓展。 通過(guò)調(diào)用[RLMObject objectsWhere:],獲得了默認(rèn)realm數(shù)據(jù)庫(kù)中的所有顏色是黃褐色的,名字開(kāi)頭是“B”的狗的實(shí)例。

// 條件查詢(xún)
RLMResults *tanDogs = [Dog objectsWhere:@"color = 'tan' AND name BEGINSWITH 'B'"];
// 使用一個(gè)NSPredicate對(duì)象查詢(xún)
NSPredicate *pred = [NSPredicate predicateWithFormat:@"color = %@ AND name BEGINSWITH %@", @"tan", @"B"];
tanDogs = [Dog objectsWithPredicate:pred];

可以參看Apple的Predicates Programming Guide了解更多關(guān)于如何創(chuàng)建謂詞。

1.Realm支持很多常見(jiàn)的謂詞:在比較中, 操作數(shù)可以是屬性名或者常量。但是其中至少有一個(gè)是屬性名。
2.只有int, long, float, double, and NSDate這些屬性類(lèi)型(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.在很多情況下,我們都希望獲取或者查詢(xún)返回的結(jié)果都能按照一定條件排序。
所以,RLMArray支持使用指定的屬性對(duì)數(shù)據(jù)列進(jìn)行排序。
2.Realm允許你指定一個(gè)排序要求并且根據(jù)一個(gè)或多個(gè)屬性進(jìn)行排序。
3.舉例來(lái)說(shuō), 下面代碼呼叫了[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)讲樵?xún)

  • Realm查詢(xún)引擎的一個(gè)獨(dú)特屬性就是它能夠進(jìn)行簡(jiǎn)單快捷的鏈?zhǔn)讲樵?xún), 而不需要像傳統(tǒng)數(shù)據(jù)庫(kù)一樣的麻煩。舉個(gè)例子來(lái)說(shuō),假如你要所有黃褐色的小狗的結(jié)果序列,然后從中查找名字開(kāi)頭是“B“的小狗。 你可以發(fā)送如下的請(qǐng)求。
RLMResults *tanDogs = [Dog objectsWhere:@"color = 'tan'"];
RLMResults *tanDogsWithBNames = [tanDogs objectsWhere:@"name BEGINSWITH 'B'"];

5.刪除數(shù)據(jù)

  • 刪除某個(gè)在Realm數(shù)據(jù)庫(kù)中的數(shù)據(jù)。
Book *cheeseBook = ... // 存儲(chǔ)在 Realm 中的 Book 對(duì)象
// 在事務(wù)中刪除一個(gè)對(duì)象
[realm beginWriteTransaction];
[realm deleteObject:cheeseBook];
[realm commitWriteTransaction];
  • 刪除數(shù)據(jù)庫(kù)中的所有數(shù)據(jù)。
// 從 Realm 中刪除所有數(shù)據(jù)
[realm beginWriteTransaction];
[realm deleteAllObjects];
[realm commitWriteTransaction];

十.通知

每當(dāng)一次寫(xiě)事務(wù)完成Realm實(shí)例都會(huì)向其他線程上的實(shí)例發(fā)出通知,可以通過(guò)注冊(cè)一個(gè)block來(lái)響應(yīng)通知:

// Observe Realm Notifications
self.token = [realm addNotificationBlock:^(NSString *note, RLMRealm * realm) {
[myViewController updateUI];
}];

只要有任何的引用指向這個(gè)返回的notification token,它就會(huì)保持激活狀態(tài)。
在這個(gè)注冊(cè)更新的類(lèi)里,你需要有一個(gè)強(qiáng)引用來(lái)鉗制這個(gè)token, 因?yàn)橐坏﹏otification token被釋放,通知也會(huì)自動(dòng)解除注冊(cè)。

具體內(nèi)容:
[Realm addNotificationBlock:]和[Realm removeNotificationBlock:]

十一.Realm配置

每個(gè)用戶(hù)有自己不同的數(shù)據(jù)庫(kù),在App啟動(dòng)以后根據(jù)用戶(hù)的uid來(lái)設(shè)置數(shù)據(jù)庫(kù),可以通過(guò)對(duì)默認(rèn)配置進(jìn)行更改,然后通過(guò)訪問(wèn)默認(rèn)數(shù)據(jù)庫(kù)來(lái)實(shí)現(xiàn)不同用戶(hù)不同數(shù)據(jù)庫(kù).
因?yàn)樵O(shè)置了模型插入數(shù)據(jù)庫(kù)以后如果發(fā)生屬性更改,需要進(jìn)行版本遷移.可以使用app的版本作為數(shù)據(jù)庫(kù)的版本,當(dāng)版本迭代發(fā)生以后,改了模型的屬性,通過(guò)更改App的版本號(hào)實(shí)現(xiàn)版本遷移.

// 版本遷移和配置數(shù)據(jù)庫(kù)基本數(shù)據(jù)
- (void)setRealmMigration:(NSString *)username{
    RLMRealmConfiguration *config = [RLMRealmConfiguration defaultConfiguration];
    
    //    BTLog(@"%@---",config.fileURL);
    // 使用默認(rèn)的目錄,但是使用用戶(hù)名來(lái)替換默認(rèn)的文件名
    config.fileURL = [[[config.fileURL URLByDeletingLastPathComponent]
                       URLByAppendingPathComponent:username ? username : @"defalut"]
                      URLByAppendingPathExtension:@"realm"];
    
    // 將這個(gè)配置應(yīng)用到默認(rèn)的 Realm 數(shù)據(jù)庫(kù)當(dāng)中
    [RLMRealmConfiguration setDefaultConfiguration:config];
    
    // 設(shè)置新的架構(gòu)版本。這個(gè)版本號(hào)必須高于之前所用的版本號(hào)(如果您之前從未設(shè)置過(guò)架構(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ì)在打開(kāi)低于上面所設(shè)置版本號(hào)的 Realm 數(shù)據(jù)庫(kù)的時(shí)候被自動(dòng)調(diào)用
    config.migrationBlock = ^(RLMMigration *migration, uint64_t oldSchemaVersion) {
        // 目前我們還未進(jìn)行數(shù)據(jù)遷移,因此 oldSchemaVersion == 0
        if (oldSchemaVersion < schemaVersion) {
            // 什么都不要做!Realm 會(huì)自行檢測(cè)新增和需要移除的屬性,然后自動(dòng)更新硬盤(pán)上的數(shù)據(jù)庫(kù)架構(gòu)
        }
    };
    // 告訴 Realm 為默認(rèn)的 Realm 數(shù)據(jù)庫(kù)使用這個(gè)新的配置對(duì)象
    [RLMRealmConfiguration setDefaultConfiguration:config];
    
    // 現(xiàn)在我們已經(jīng)告訴了 Realm 如何處理架構(gòu)的變化,打開(kāi)文件之后將會(huì)自動(dòng)執(zhí)行遷移
    [RLMRealm defaultRealm];
    
 // realm文件的位置   
    BTLog(@"fileurl===%@",[RLMRealmConfiguration defaultConfiguration].fileURL);
}
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容