2018-04-16

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. 创建数据模型

如图创建数据模型非常方便,跟创建普通的模型没有区别。 

创建后的.h和.m

我们需要在.h报错的地方添加属性以及自定义方法。添加的属性必须是Realm支持的基本数据类型或继承于RLMObject类型。在.m中defaltPropertyValues方法中设置默认值,在ignoredProperties方法中设置不保存到数据库的属性。

项目中使用场景是创建一个用户User数据模型,这个模型里面保存有用户信息、每日的运动记录信息、还有用户保存的拍摄视频信息,User代码如下:

User.h文件

#import

#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 *sportDayItems;

@property RLMArray *videoItems;

+ (User *)getLastUser;

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

@end

// This protocol enables typed collections. i.e.:

// RLMArray

RLM_ARRAY_TYPE(User)

User.m文件

#import "User.h"

#import

@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 和 RLMObject的子类来建立诸如一对多、一对一之类的关系模型。

PS:从支持类型中能发现并不支持NSUInteger,NSUInteger类型常用于枚举类型,对于Realm不支持的类型,编译并不会报错,但运行会直接崩溃。用户信息属性UserInforItem是继承于RLMObject。

主键

User.h的identification属性作为主键,需要在.m的+ (NSString *)primaryKey 返回identification属性名。

+ (NSString *)primaryKey {

      return @"identification";

    }

默认属性

实现defaultPropertyValues方法,需要返回属性名的键值对字典。创建User数据模型时,会自动设置这些属性的默认值。若不需要设置默认值,则可以注释该方法。

+ (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使用技巧,均可留言互相交流探讨。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 219,490评论 6 508
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,581评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 165,830评论 0 356
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,957评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,974评论 6 393
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,754评论 1 307
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,464评论 3 420
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,357评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,847评论 1 317
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,995评论 3 338
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,137评论 1 351
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,819评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,482评论 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,023评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,149评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,409评论 3 373
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,086评论 2 355

推荐阅读更多精彩内容