淺談Realm數據庫及其使用

1.Realm介紹

realm是一個跨平臺移動數據庫引擎,支持iOS、OS X(Objective-C和Swift)以及Android。目前還支持React Native 和 Xamarin。

2014年7月發佈。由YCombinator孵化的創業團隊歷時幾年打造,是第一個專門針對移動平臺設計的數據庫。目標是取代SQLite。

爲了完全解決性能問題,核心數據引擎C++打造,並非創建在SQLite之上的ORM。

2.爲什麼使用Realm

a. 快速
引用官網上性能比較數據柱狀圖,每秒能在200k條記錄的數據庫查詢到的數據記錄達到30條,Realm的性能遠超SQL、FMDB、CoreData的性能。以下:

b. 易用、簡潔
1)Realm提供多種版本支持OC、swift等多種語言。

2)建立儲存對象簡單,僅需集成Realm類;或者經過Realm提供的軟件導入到Xcode,能夠直接建立儲存對象,無需增長額外代碼。

3)相比SQL,不須要記住繁雜SQL語句。

4)相比CoreData,不須要架構類與類之間的複雜關係;代碼更加簡潔。學習成本更低。

3.如何使用Realm(OC版本)

a. 準備工做

(1) 下載最新的Realm發行版本,並解壓;

(2) 前往Xcode 工程的」General」設置項中,從ios/dynamic/、osx/、tvos/或者watchos/中將’Realm.framework’拖曳到」Embedded Binaries」選項中。確認Copy items if needed被選中後,點擊Finish按鈕;

(3) 在單元測試目標的」Build Settings」中,在」Framework Search Paths」中添加Realm.framework的上級目錄;

(4) 下載一個名爲 Realm Browser 的獨立的Mac應用以便 對.realm數據庫進行讀取和編輯。

(5) 安裝 Realm 插件
打開[release.zip](https://static.realm.io/downloads/objc/realm-objc-1.0.1.zip) 中的plugin/RealmPlugin.xcodeproj並進行編譯,重啓 Xcode以後插件便可生效。若是您使用 Xcode 菜單來創建一個新文件(File > New > File… — or ⌘N) ,您就能夠看到有一個新建Realm模型的選項。

b. 建立數據模型

如圖建立數據模型很是方便,跟建立普通的模型沒有區別。
ios

建立後的.h和.m數據庫


咱們須要在.h報錯的地方添加屬性以及自定義方法。添加的屬性必須是Realm支持的基本數據類型或繼承於RLMObject類型。在.m中defaltPropertyValues方法中設置默認值,在ignoredProperties方法中設置不保存到數據庫的屬性。swift

項目中使用場景是建立一個用戶User數據模型,這個模型裏面保存有用戶信息、每日的運動記錄信息、還有用戶保存的拍攝視頻信息,User代碼以下:數組

User.h文件xcode

#import <Realm/Realm.h>
#import "UserInforItem.h"
#import "SportDayItem.h"
#import "SportInforItem.h"
#import "DataModel.h"
#import "VideoItem.h"

@interface User : RLMObject
@property NSString * identification;
@property UserInforItem *userInforItem;
@property RLMArray<SportDayItem> *sportDayItems;
@property RLMArray<VideoItem> *videoItems;

+ (User *)getLastUser;

- (void)addSportInforItem:(SportInforItem *)sportInforItem;


@end

// This protocol enables typed collections. i.e.:
// RLMArray<User>
RLM_ARRAY_TYPE(User)

User.m文件數據結構

#import "User.h"
#import <NSDate+DateTools.h>

@implementation User

// Specify default values for properties

+ (NSDictionary *)defaultPropertyValues
{
    return @{
             @"userInforItem":@{
                                 @"name": @"",
                                 @"location": @"",
                                 @"genderType": @0,
                                 @"height": @"170",
                                 @"weight": @"60",
                                 @"birth": @"1995-01-01",
                                 @"signature": @"智能運動,引領時尚",
                                 @"headIconPath": @"",
                                 @"phoneNum": @"",
                                 @"userID": @"",
                                 @"creatTime": [NSDate date],
                                 @"lasLoginTime": @"",
                                 @"lastLoginVersion": @"",
                                 @"isWiFi":@NO
                                },
              @"sportDayItems":@[],
              @"videoItems":@[]
             };
}

+ (NSString *)primaryKey {
    return @"identification";
}

#pragma mark - Public Method

+ (User *)getLastUser
{
    NSString *identification = [DataModel lastLoginID];
    User *user = [User objectForPrimaryKey:identification];
    if (user == nil) {
        user = [[User alloc] init];
        user.identification = [DataModel lastLoginID];
        RLMRealm *realm = [RLMRealm defaultRealm];
        [realm beginWriteTransaction];
        [realm addObject:user];
        [realm commitWriteTransaction];
    }
    
    return user;
}

- (void)addSportInforItem:(SportInforItem *)sportInforItem
{
    BOOL isHaveTodaySportRecord = NO;
    for (SportDayItem *dayItem in self.sportDayItems) {
        if ([dayItem.sportDate dayOfYear] == [sportInforItem.creatTime dayOfYear]) {
            isHaveTodaySportRecord = YES;
            RLMRealm *realm = [RLMRealm defaultRealm];
            [realm beginWriteTransaction];
            [dayItem.sportInforArray addObject:sportInforItem];
            [realm commitWriteTransaction];
            break;
        }
    }
    
    if (!isHaveTodaySportRecord) {
        SportDayItem *dayItem = [[SportDayItem alloc] init];
        dayItem.sportDate = sportInforItem.creatTime;
        [dayItem.sportInforArray addObject:sportInforItem];
        RLMRealm *realm = [RLMRealm defaultRealm];
        [realm beginWriteTransaction];
        [self.sportDayItems addObject:dayItem];
        [realm commitWriteTransaction];
    }
}

// Specify properties to ignore (Realm won't persist these)

//+ (NSArray *)ignoredProperties
//{
//    return @[];
//}

@end
屬性
Realm支持如下的屬性類型:BOOL、bool、int、NSInteger、long、long long、float、double、NSString、NSDate、NSData 以及 被特殊類型標記的 NSNumber 。

CGFloat 屬性的支持被取消了,由於它的類型不依賴於平臺。

您可使用RLMArray<Object *><Object> 和 RLMObject的子類來創建諸如一對多、一對一之類的關係模型。

PS:從支持類型中能發現並不支持NSUInteger,NSUInteger類型經常使用於枚舉類型,對於Realm不支持的類型,編譯並不會報錯,但運行會直接崩潰。用戶信息屬性UserInforItem是繼承於RLMObject。
主鍵

User.h的identification屬性做爲主鍵,須要在.m的+ (NSString *)primaryKey 返回identification屬性名。架構

+ (NSString *)primaryKey {
        return @"identification";
    }
默認屬性

實現defaultPropertyValues方法,須要返回屬性名的鍵值對字典。建立User數據模型時,會自動設置這些屬性的默認值。若不須要設置默認值,則能夠註釋該方法。ide

+ (NSDictionary *)defaultPropertyValues
{
    return @{
             @"userInforItem":@{
                                 @"name": @"",
                                 @"location": @"",
                                 @"genderType": @0,
                                 @"height": @"170",
                                 @"weight": @"60",
                                 @"birth": @"1995-01-01",
                                 @"signature": @"智能運動,引領時尚",
                                 @"headIconPath": @"",
                                 @"phoneNum": @"",
                                 @"userID": @"",
                                 @"creatTime": [NSDate date],
                                 @"lasLoginTime": @"",
                                 @"lastLoginVersion": @"",
                                 @"isWiFi":@NO
                                },
              @"sportDayItems":@[],
              @"videoItems":@[]
             };
}
忽略屬性(不保存到數據庫)

某些場景中,數據模型的一些屬性僅做爲中間變量,不須要保存到數據庫中。這時候咱們能夠實現ignoredProperties方法,返回一個數組包含對應的屬性名便可。性能

// Specify properties to ignore (Realm won't persist these)

//+ (NSArray *)ignoredProperties
//{
//    return @[];
//}
查詢數據

(1)經過主鍵查找單元測試

+ (User *)getLastUser
{
    NSString *identification = [DataModel lastLoginID];
    User *user = [User objectForPrimaryKey:identification];
    if (user == nil) {
        user = [[User alloc] init];
        user.identification = [DataModel lastLoginID];
        RLMRealm *realm = [RLMRealm defaultRealm];
        [realm beginWriteTransaction];
        [realm addObject:user];
        [realm commitWriteTransaction];
    }
    
    return user;
}

經過objectForPrimaryKey方法從數據庫中獲取到User,若是取出來的是nil,就經過以下方法拿到數據庫單例,執行beginWriteTransaction方法和
commitWriteTransaction方法添加新的user對象到數據庫。須要注意的是beginWriteTransaction方法和commitWriteTransaction方法要配對使用,不然不能存到數據庫。

RLMRealm *realm = [RLMRealm defaultRealm];
        [realm beginWriteTransaction];
        [realm addObject:user];
        [realm commitWriteTransaction];

(2)經過NSPredicate查詢

經過斷言查找到2016年的運動記錄,RLMResults可看做是數組

NSPredicate *pred = [NSPredicate predicateWithFormat:@"dayOfYear = %@,@"2016"];
RLMResults *thisYearSportItems = [sportDayItems objectsWithPredicate:pred];
修改數據

經過addObject方法將本次運動數據添加到今天運動記錄。同理,經過deleteObject刪除數據。

RLMRealm *realm = [RLMRealm defaultRealm];
[realm beginWriteTransaction];
[dayItem.sportInforArray addObject:sportInforItem];
[realm commitWriteTransaction];
數據庫遷移

當數據模型增長新屬性或者修改屬性都須要進行數據庫遷移。在Appdelegate的didFinishLaunchingWithOptions方法中實現以下代碼:

#pragma mark - 數據庫遷移
- (void)migrationRealm
{
    RLMRealmConfiguration *config = [RLMRealmConfiguration defaultConfiguration];
    config.schemaVersion = 2;
    config.migrationBlock = ^(RLMMigration *migration, uint64_t oldSchemaVersion) {
        // 目前咱們還未進行數據遷移,所以 oldSchemaVersion == 0
        if (oldSchemaVersion < 1) {
            //低於版本1,執行相應遷移代碼(按版本遷移代碼:按需可選,若增長新屬性或刪除,可不寫)
        }
        if (oldSchemaVersion < 2) {
            //低於版本2,執行相應遷移代碼(按版本遷移代碼:按需可選,若增長新屬性或刪除,可不寫)
        }
    };
    [RLMRealmConfiguration setDefaultConfiguration:config];
    [RLMRealm defaultRealm];

}

Realm進行數據遷移是很是方便的。須要把數據庫版本+1.若是隻是增長或刪除屬性,按版本的遷移代碼不須要寫。Realm會自動進行數據結構調整。按版本遷移代碼是高級特性,使用場景是把數據模型的幾個舊屬性合併成一個新屬性。

PS:沒有進行遷移的數據庫版本schemaVersion爲0,第一次遷移的數據庫版本設置爲1.

結語

Realm數據庫是一個面向對象、簡單易用,性能強大的數據庫,還有不少高級特性須要慢慢學習。我的以爲學習成本較低,用起來比較順手的,加入項目中僅增長1M。但剛開始的時候很容易踩坑,由於Realm報錯信息常常讓人摸不着邊。如在使用Realm的過程當中遇到任何難解問題,或者你有更好的Realm使用技巧,都可留言互相交流探討。

相關文章
相關標籤/搜索