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