ios,简单使用realm数据库
Realm
Realm-Object-c,见:
https://realm.io/cn/docs/objc/latest/
Realm官网:
https://realm.io
介绍
Realm可以轻松地移植到项目当中,并且绝大部分常用的功能(比如说插入、查询等等)都可以用一行简单的代码轻松完成!目前支持Objective-C、Swift和Java三种语言,也就是说能在iOS、Android和Mac上面跨平台使用。
优点
-
Easy to Use(简单易用):Core Data和SQLite冗余、繁杂的知识和代码足以吓退绝大多数刚入门的开发者,而换用Realm,则可以极大地减少学习代价和学习时间,让应用及早用上数据存储功能。
-
Cross-Platform(跨平台):现在绝大多数的应用开发并不仅仅只在iOS平台上进行开发,还要兼顾到Android平台的开发。为两个平台设计不同的数据库是愚蠢的,而使用Realm数据库,iOS和Android无需考虑内部数据的架构,调用Realm提供的API就可以完成数据的交换,实现“一个数据库,两个平台无缝衔接”。
-
Fast(高效):Realm相比使用CoreData和原生的SQLite来说速度更快更加高效,而且代码量更少。
快速集成Realm
1、下载最新的 Realm 更新包,解压zip文件
2、将 ios/static
目录下面的 Realm.framework
文件拖到项目里面(确保Copy items if needed选中)
3、在 target -> Build Phases -> Link Binary with Libraries
中添加 libc++.dylib
说明:
(1)对于使用Swift的童鞋,请讲Swift/RLMSupport.swift文件拖到项目中(确保Copy items if needed选中)
(2)推荐使用Cocoapods进行安装,在Podfile中添加 pod 'Realm'
即可
(3)也可以自行到Github上面下载代码进行编译,此处不作过多的介绍
运行环境:
(1)支持 >= iOS7.0, >= OS X 10.9, 及WatchKit
(2)推荐使用Xcode 5以上的IDE,支持Swift
辅助工具和插件的安装
1、Realm Browser
Realm官方非常贴心的向开发者提供了一个用于查看喝编辑Realm数据的工具 Realm Browser
.
2、Xcode Plugin(Xcode8之后好像苹果大大不再支持Xcode插件,如果硬是要使用插件,还是有办法的,请自行谷歌)
在Realm中使用到最多的是Realm Model(继承自RLMObject的类,后面有介绍)。官方提供了一个Xcode的插件让我们在创建模型变得非常轻松
安装使用:
(1)最简单的安装方式是通过Alcatraz,搜索 RealmPlugin
直接安装
(2)或者是打开zip文件夹下面的 plugin/RealmPlguin.xcodeproj
,build一下就安装好了
安装完后重启Xcode生效,在创建model的时候选择New File(或⌘N),选择Realm按照要求输入model的名字就OK啦。
Realm的使用
1、构建数据库
Realm提供了三种方式创建数据库,一种是存储在默认路径下的数据库,一种是我们可以自己指定数据库文件的存储路径和只读属性,另外还可以使用内存数据库。
(1)默认Realm数据库
RLMRealm *realm = [RLMRealm defaultRealm];
可以通过: [RLMRealm defaultRealmPath]
查看默认存储的路径。
(2)自定义Realm数据库
NSString *docPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];
NSString *dbPath = [docPath stringByAppendingPathComponent:@"db/db.realm"];
RLMRealm *realm = [RLMRealm realmWithPath:dbPath];
或
NSString *docPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];
NSString *dbPath = [docPath stringByAppendingPathComponent:@"db/db.realm"];
RLMRealm *realm = [RLMRealm realmWithPath:dbPath readOnly:YES error:nil];
其中readOnly表示创建的数据库是只读数据库。
(3)内存数据库
正常的Realm数据库是存储在硬盘上的, 但你也可以通过使用 + (instancetype)inMemoryRealmWithIdentifier:(NSString *)identifier;
来创建一个内存数据库。
RLMRealm *realm = [RLMRealm inMemoryRealmWithIdentifier:@"test"];
(4) RLMRealmConfiguration
那要如何构建非默认Realm对象呢?答案是RLMRealmConfiguration,通过调用:
+ (nullable instancetype) realmWithConfiguration:(nonnull RLMRealmConfiguration *)configuration error:(NSError *_Nullable *_Nullable)error;
可以构造一个用configuration配置过后的Realm对象,其主要可配置项有:
成员 | 意义 |
---|---|
fileURL | 数据库文件位置 |
inMemoryIdentifier | 如果设置的话,数据将存放在内存中并以此为标记 |
readOnly | 是否只读 |
encryptionKey | 如果设置的会用其作为key对数据做AES加密 |
schemaVersion | 数据库内容的版本号 |
migrationBlock | 如果做版本兼容的话,如果转换的逻辑block |
deleteRealmIfMigrationNeeded | 当数据库内容版本不兼容时是否重新创建 |
作者:CZ_iOS
链接:https://www.jianshu.com/p/52a9f84b158f
来源:简书
简书著作权归作者所有,任何形式的转载都请联系作者获得授权并注明出处。
2、数据模型
Realm的数据模型是用传统的Objective-C接口(interface)和属性(@property)定义的。 只要定义 RLMObject
的一个子类或者一个现成的模型类,你就能轻松创建一个Realm的数据模型对象。Realm模型对象和其他的Objective-c的功能很相似–你可以给它们添加你自己的方法和protocol然后和其他的对象一样使用。 唯一的限制就是从它们被创建开始,只能在一个进程中被使用。
如果已经安装了Realm Xcode插件,在 New File
对话框中会有一个很漂亮的样板,你可以用它来创建interface和implementation文件。
数据模型
创建简单数据模型
简单地,继承RLMObject创建类,在.h中通过属性定义不同的内容.
RLMResults <0x7fe5e3d22ec0>(
[0]Data {
time = 2016-01-08 05:51:12 +0000;
title = test;
}
)
生成如上的数据结构,只需创建类如下:
// .h
#import <Realm/Realm.h>
@interface Data : RLMObject
@property (nonatomic , strong) NSDate *time;
@property (nonatomic , copy) NSString *title;
@end
// .m
#import "Data.h"
@implementation Data
@end
生成主键(Primary Keys)
重写 +primaryKey 可以设置模型的主键。声明主键之后,对象将被允许查询,更新速度更加高效,并且要求每个对象保持唯一性。 一旦带有主键的对象被添加到 Realm 之后,该对象的主键将不可修改。
@interface Person : RLMObject
@property NSInteger id;
@property NSString *name;
@end
@implementation Person
+ (NSString *)primaryKey {
return @"id";
}
@end
忽略属性(Ignored Properties)
重写 +ignoredProperties 可以防止 Realm 存储数据模型的某个属性。Realm 将不会干涉这些属性的常规操作,它们将由成员变量(ivar)提供支持,并且您能够轻易重写它们的 setter 和 getter。
@interface Person : RLMObject
@property NSInteger tmpID;
@property (readonly) NSString *name; // 只读属性将被自动忽略
@property NSString *firstName;
@property NSString *lastName;
@end
@implementation Person
+ (NSArray *)ignoredProperties {
return @[@"tmpID"];
}
- (NSString *)name {
return [NSString stringWithFormat:@"%@ %@", self.firstName, self.lastName];
}
@end
属性默认值
重写+defaultPropertyValues可以在每次对象创建之后为其提供默认值。
@interface Book : RLMObject
@property float price;
@property NSString *title;
@end
@implementation Book
+ (NSDictionary *)defaultPropertyValues {
return @{@"price" : @0, @"title": @""};
}
@end
创建嵌套数据模型
数据结构:Person具有name、birthdate、dogs,Dog具有name、owner< Person >。
RLMResults <0x7fb431f2bd60> (
[0] Person {
name = crylown;
birthdate = 2016-01-08 06:02:23 +0000;
dogs = RLMArray <0x7fb431f2fcd0> (
[0] Dog {
name = myDog;
owner = Person {
name = crylown;
birthdate = 2016-01-08 06:02:23 +0000;
dogs = <Maximum depth exceeded>;
};
},
[1] Dog {
name = yourDog;
owner = Person {
name = crylown;
birthdate = 2016-01-08 06:02:23 +0000;
dogs = <Maximum depth exceeded>;
};
}
);
}
)
生成如上的数据结构,创建数据模型代码如下:
// .h
#import <Realm/Realm.h>
@class Person;
// 狗狗的数据模型
@interface Dog : RLMObject
@property NSString *name;
@property Person *owner;
@end
RLM_ARRAY_TYPE(Dog) // 定义RLMArray<Dog>
// 狗狗主人的数据模型
@interface Person : RLMObject
@property NSString *name;
@property NSDate *birthdate;
// 通过RLMArray建立关系
@property RLMArray<Dog> *dogs;
@end
RLM_ARRAY_TYPE(Person) // 定义RLMArray<Person>
// .m
@implementation Dog
@end // 暂无使用
@implementation Person
@end // 暂无使用
使用Realm数据模型
创建对象
一般属性
Data *data = [[Data alloc] init];
data.time = [NSDate dateWithTimeIntervalSinceNow:0];
data.title = @"test";
等价于
//字典
Data *data = [[Data alloc] initWithValue:@{@"time":[NSDate dateWithTimeIntervalSinceNow:0],@"title":@"test"}];
等价于
//数组
Data *data = [[Data alloc] initWithValue:@[[NSDate dateWithTimeIntervalSinceNow:0],@"test"]];
嵌套属性
Person *me = [[Person alloc] init];
Dog *myDog = [[Dog alloc] init];
myDog.name = @"myDog";
myDog.owner = me;
Dog *yourDog = [[Dog alloc] init];
yourDog.name = @"yourDog";
yourDog.owner = me;
me.name = @"crylown";
me.birthdate = [NSDate dateWithTimeIntervalSinceNow:1];
// 将dog添加到 <RLMArray>dogs属性中
[me.dogs addObject:myDog];
[me.dogs addObject:yourDog];
等价于
Dog *myDog = [[Dog alloc] init];
myDog.name = @"myDog";
Dog *yourDog = [[Dog alloc] init];
yourDog.name = @"yourDog";
Person *me = [[Person alloc] initWithValue:@[@"crylown",[NSDate dateWithTimeIntervalSinceNow:1],@[myDog,yourDog]]];
yourDog.owner = me;
myDog.owner = me;
等价于
// 多重嵌套
// dog.owner无法在这里设置
Person *me = [[Person alloc] initWithValue:@[@"crylown",[NSDate dateWithTimeIntervalSinceNow:1],@[@[@"myDog"],@[@"yourDog"]]]];
说明:
(1)Realm支持以下的属性(property)种类:BOOL, bool, int, NSInteger, long, float, double, CGFloat, NSString, NSDate 和 NSData。
(2)你可以使用 RLMArray<Object>
和 RLMObject
来模拟对一或对多的关系(Realm也支持RLMObject继承)
(3)Realm忽略了Objective-C的property attributes(如nonatomic, atomic, strong, copy, weak 等等)。 所以,推荐在创建模型的时候不要使用任何的property attributes。但是,假如你设置了,这些attributes会一直生效直到RLMObject被写入realm数据库。
(4)定义了 RLM_ARRAY_TYPE(Article)
这个宏表示支持 RLMArray<Article>
该属性
(5)另外Realm提供了以下几个方法供对属性进行自定义:
1) + (NSArray *)indexedProperties;
: 可以被重写来来提供特定属性(property)的属性值(attrbutes)例如某个属性值要添加索引。
2) + (NSDictionary *)defaultPropertyValues;
: 为新建的对象属性提供默认值。
3) + (NSString *)primaryKey;
: 可以被重写来设置模型的主键。定义主键可以提高效率并且确保唯一性。
4) + (NSArray *)ignoredProperties;
:可以被重写来防止Realm存储模型属性。
使用Realm进行数据管理
在数据管理的过程中,常用的方法有:
RLMRealm *realm = [RLMRealm defaultRealm];
// 开放RLMRealm事务
[realm beginWriteTransaction];
// 在开放开放/提交事务之间进行数据处理
// 提交事务
[realm commitWriteTransaction];
等价于
[realm transactionWithBlock:^{
// 进行数据处理
}];
添加数据
RLMRealm *realm = [RLMRealm defaultRealm];
// 开放RLMRealm事务
[realm beginWriteTransaction];
// 添加到数据库 me为RLMObject
[realm addObject:me];
// 提交事务
[realm commitWriteTransaction];
查询数据
数据库查询
// 查询默认的 Realm 数据库
RLMResults *dogs = [Dog allObjects]; // 从默认的 Realm 数据库中,检索所有狗狗
如果有需要,也可以查询指定的数据库
// 查询指定的 Realm 数据库
RLMRealm *petsRealm = [RLMRealm realmWithPath:@"pets.realm"]; // 获得一个指定的 Realm 数据库
RLMResults *otherDogs = [Dog allObjectsInRealm:petsRealm]; // 从该 Realm 数据库中,检索所有狗狗
条件查询
1.使用断言字符串查询:
RLMResults *tanDogs = [Dog objectsWhere:@"color = '棕黄色' AND name BEGINSWITH '大'"];
2.// 使用 NSPredicate 查询
NSPredicate *pred = [NSPredicate predicateWithFormat:@"color = %@ AND name BEGINSWITH %@",
@"棕黄色", @"大"];
RLMResults *tanDogs = [Dog objectsWithPredicate:pred];
3.链式查询
如果我们想获得获得棕黄色狗狗的查询结果,并且在这个查询结果的基础上再获得名字以“大”开头的棕黄色狗狗。
RLMResults *tanDogs = [Dog objectsWhere:@"color = '棕黄色'"];
RLMResults *tanDogsWithBNames = [tanDogs objectsWhere:@"name BEGINSWITH '大'"];
修改数据
内容直接更新
// author为数据库内RLMObject
[realm beginWriteTransaction];
author.name = @"托马斯·品钦";
[realm commitWriteTransaction];
根据主键更新
如果您的数据模型中设置了主键的话,那么您可以使用+[RLMObject createOrUpdateInRealm:withValue:]来更新对象,或者当对象不存在时插入新的对象。
// 创建一个带有主键的“书籍”对象,作为事先存储的书籍
Book *cheeseBook = [[Book alloc] init];
cheeseBook.title = @"奶酪食谱";
cheeseBook.price = @9000;
cheeseBook.id = @1;
// 通过 id = 1 更新该书籍
[realm beginWriteTransaction];
[Book createOrUpdateInRealm:realm withValue:cheeseBook];
[realm commitWriteTransaction];
键值编码
RLMObject、RLMResult 以及 RLMArray 都遵守键值编码(Key-Value Coding)(KVC)机制。
RLMResults *persons = [Person allObjects];
[[RLMRealm defaultRealm] transactionWithBlock:^{
[[persons firstObject] setValue:@YES forKeyPath:@"isFirst"];
// 将每个人的 planet 属性设置为“地球”
[persons setValue:@"地球" forKeyPath:@"planet"];
}];
删除数据
删除某个在Realm数据库中的数据。
Book *cheeseBook = ... // 存储在 Realm 中的 Book 对象
// 在事务中删除一个对象
[realm beginWriteTransaction];
[realm deleteObject:cheeseBook];
[realm commitWriteTransaction];
删除数据库中的所有数据。
// 从 Realm 中删除所有数据
[realm beginWriteTransaction];
[realm deleteAllObjects];
[realm commitWriteTransaction];
数据排序
RLMResults 允许您指定一个排序标准,从而可以根据一个或多个属性进行排序。比如说,下列代码将上面例子中返回的狗狗根据名字升序进行排序:
// 排序名字以“大”开头的棕黄色狗狗
RLMResults *sortedDogs = [[Dog objectsWhere:@"color = '棕黄色' AND name BEGINSWITH '大'"]
sortedResultsUsingProperty:@"name" ascending:YES];
通知
每当一次写事务完成Realm实例都会向其他线程上的实例发出通知,可以通过注册一个block来响应通知:
self.token = [realm addNotificationBlock:^(NSString *note, RLMRealm * realm) {
[_listTableView reloadData];
}];
只要有任何的引用指向这个返回的notification token,它就会保持**状态。在这个注册更新的类里,你需要有一个强引用来钳制这个token, 因为一旦notification token被释放,通知也会自动解除注册。
@property (nonatomic, strong) RLMNotificationToken *token;
另外可以使用下面的方式解除通知:
[realm removeNotification:self.token];
Realm配置
通过RLMRealmConfiguration您可以配置诸如 Realm 文件在何处存储之类的信息。
配置同时也可以在每次您需要使用 Realm 实例的时候传递给[RLMRealm realmWithConfiguration:config error:&err],或者您也可以通过 [RLMRealmConfiguration setDefaultConfiguration:config] 来为默认的 Realm 数据库进行配置。
比如说,假设有这样一个应用,用户必须登录到您的网站后台才能够使用,然后您希望这个应用支持快速帐号切换功能。 您可以为每个帐号创建一个特有的 Realm 文件,通过对默认配置进行更改,就可以直接使用默认的 Realm 数据库来直接访问了,如下所示:
+ (void)setDefaultRealmForUser:(NSString *)username {
RLMRealmConfiguration *config = [RLMRealmConfiguration defaultConfiguration];
// 使用默认的目录,但是使用用户名来替换默认的文件名
config.path = [[[config.path stringByDeletingLastPathComponent]
stringByAppendingPathComponent:username]
stringByAppendingPathExtension:@"realm"];
// 将这个配置应用到默认的 Realm 数据库当中
[RLMRealmConfiguration setDefaultConfiguration:config];
}
其他的Realm数据库
有的时候,在不同位置存储多个 Realm 数据库是十分有用的。 例如,如果您需要将您应用的某些数据打包到一个 Realm 文件中,作为主要 Realm 数据库的扩展。 您可以像以下代码这样做:
RLMRealmConfiguration *config = [RLMRealmConfiguration defaultConfiguration];
// 获取需要打包文件的路径
config.path = [[NSBundle mainBundle] pathForResource:@"MyBundledData" ofType:@"realm"];
// 以只读模式打开文件,因为应用数据包并不可写
config.readOnly = YES;
// 通过配置打开 Realm 数据库
RLMRealm *realm = [RLMRealm realmWithConfiguration:config];
// 从打包的 Realm 数据库中读取某些数据
RLMResults<Dog *> *dogs = [Dog objectsInRealm:realm where:@"age > 5"];
请注意,使用自定义路径来初始化 Realm 数据库需要拥有路径所在位置的写入权限。 通常存储可写 Realm 文件的地方是位于 iOS 上的“Documents”文件夹以及位于 OS X 上的“Application Support”文件夹。 具体情况,请遵循苹果的 iOS 数据存储指南, 它推荐将文件存储在<Application_Home>/Library/Caches目录下
错误处理
在数据的处理中可能会出现失败的情况,在查看错误的时候,有相关方法可以使用:
提交事务失败
[realm commitWriteTransaction:(NSError * _Nullable __autoreleasing * _Nullable)]
也可以使用
[realm transactionWithBlock:^{
} error:(NSError * _Nullable __autoreleasing * _Nullable)]
配置数据库失败
RLMRealm *realm = [RLMRealm realmWithConfiguration:config error:&error];
要处理在指定线程中初次 Realm 数据库导致的错误, 给 error 参数提供一个 NSError 指针。
数据迁移
当您使用任意一个数据库时,您随时都可能打算修改您的数据模型。通过设置 RLMRealmConfiguration.schemaVersion 以及RLMRealmConfiguration.migrationBlock 可以定义一个迁移操作以及与之关联的架构版本。 迁移闭包将会提供提供相应的逻辑操作,以让数据模型从之前的架构转换到新的架构中来。 每当通过配置创建完一个 RLMRealm 之后,迁移闭包将会在迁移需要的时候,将给定的架构版本应用到更新 RLMRealm 操作中。
如下所示是最简单的数据迁移的必需流程:
// 在 [AppDelegate didFinishLaunchingWithOptions:] 中进行配置
RLMRealmConfiguration *config = [RLMRealmConfiguration defaultConfiguration];
// 设置新的架构版本。这个版本号必须高于之前所用的版本号(如果您之前从未设置过架构版本,那么这个版本号设置为 0)
config.schemaVersion = 1;
// 设置闭包,这个闭包将会在打开低于上面所设置版本号的 Realm 数据库的时候被自动调用
config.migrationBlock = ^(RLMMigration *migration, uint64_t oldSchemaVersion) {
// 目前我们还未进行数据迁移,因此 oldSchemaVersion == 0
if (oldSchemaVersion < 1) {
// 什么都不要做!Realm 会自行检测新增和需要移除的属性,然后自动更新硬盘上的数据库架构
}
};
// 告诉 Realm 为默认的 Realm 数据库使用这个新的配置对象
[RLMRealmConfiguration setDefaultConfiguration:config];
// 现在我们已经告诉了 Realm 如何处理架构的变化,打开文件之后将会自动执行迁移
[RLMRealm defaultRealm];
当你和数据库打交道的时候,时不时的你需要改变数据模型(model),但因为Realm中得数据模型被定义为标准的Objective-C interfaces,要改变模型,就像改变其他Objective-C interface一样轻而易举。举个例子,假设有个数据模型 Person
:
在v1.0中数据模型如下:
// v1.0
@interface Person : RLMObject
@property NSString *firstName;
@property NSString *lastName;
@property int age;
@end
升级到v2.0之后将firstName和lastName字段合并为一个字段fullName
// v2.0
@interface Person : RLMObject
@property NSString *fullName; // new property
@property int age;
@end
迁移的逻辑可以为:
[RLMRealm setSchemaVersion:2.0 forRealmAtPath:[RLMRealm defaultRealmPath]
withMigrationBlock:^(RLMMigration *migration,
NSUInteger oldSchemaVersion) {
[migration enumerateObjects:Person.className
block:^(RLMObject *oldObject, RLMObject *newObject) {
if (oldSchemaVersion < 2.0) {
newObject[@"fullName"] = [NSString stringWithFormat:@"%@ %@", oldObject[@"firstName"], oldObject[@"lastName"]];
}
}];
}];
当版本升级到3.0时,添加新的属性email
// v3.0
@interface Person : RLMObject
@property NSString *fullName;
@property NSString *email; // new property
@property int age;
@end
迁移的逻辑可以为:
[RLMRealm setSchemaVersion:2.0 forRealmAtPath:[RLMRealm defaultRealmPath]
withMigrationBlock:^(RLMMigration *migration,
NSUInteger oldSchemaVersion) {
[migration enumerateObjects:Person.className
block:^(RLMObject *oldObject, RLMObject *newObject) {
//处理v2.0的更新
if (oldSchemaVersion < 2.0) {
newObject[@"fullName"] = [NSString stringWithFormat:@"%@ %@", oldObject[@"firstName"], oldObject[@"lastName"]];
}
//处理v3.0的更新
if(oldSchemaVersion < 3.0) {
newObject[@"email"] = @"";
}
}];
}];
说明(摘自官方的FAQ)
1、realm的支持库有多大?
一旦你的app编译完成,realm的支持库应该只有1MB左右。我们发布的那个可能有点大(iOS ~37MB, OSX ~2.4MB), 那是因为它们还包含了对其他构架的支持(ARM,ARM64,模拟器的是X86)和一些编译符号。 这些都会在你编译app的时候被Xcode自动清理掉。
2、我应该在正式产品中使用realm吗?
自2012年起,realm就已经开始被用于正式的商业产品中了。正如你预期,我们的objective-c & Swift API 会随着社区的反馈不断的完善和进化。 所以,你也应该期待realm带给你更多的新特性和版本修复。
3、我要付realm的使用费用吗?
不要, Realm的彻底免费的, 哪怕你用于商业软件。
转自:
链接1: https://www.cnblogs.com/h-tao/p/7130416.html
链接2: https://www.jianshu.com/p/096bec929f2a