realm是一个跨平台移动数据库引擎,支持iOS、OS X(Objective-C和Swift)以及Android。目前还支持React Native 和 Xamarin。 2014年7月发布。由YCombinator孵化的创业团队历时几年打造,是第一个专门针对移动平台设计的数据库。目标是取代SQLite。 为了完全解决性能问题,核心数据引擎C++打造,并非创建在SQLite之上的ORM。
引用官网上性能比较数据柱状图,每秒能在200k条记录的数据库查询到的数据记录达到30条,Realm的性能远超SQL、FMDB、CoreData的性能。以下:
1)Realm提供多种版本支持OC、swift等多种语言。 2)建立储存对象简单,仅需集成Realm类;或者经过Realm提供的软件导入到Xcode,能够直接建立储存对象,无需增长额外代码。 3)相比SQL,不须要记住繁杂SQL语句。 4)相比CoreData,不须要架构类与类之间的复杂关系;代码更加简洁。学习成本更低。
(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模型的选项。
如图建立数据模型很是方便,跟建立普通的模型没有区别。 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使用技巧,都可留言互相交流探讨。