目录

1、Realm简介

Realm是新兴的跨平台数据库解决方案,提供多语言支持(JAVA、Objective-C、Swift、JS、.Net),你可以轻松的在iOS、Android等移动平台接入。Realm平台主要提供数据存储和云同步等服务,数据存储服务免费,云同步是收费的,不排除未来数据存储有收费的可能性,但个人认为这种可能性非常低,可以放心使用。

2、集成Realm

Realm iOS提供动态库和静态库链接方式,你可以使用Cocoapods或者Carthage快速集成, 目前最新版本为3.17.3。

使用Cocoapos集成,

pod 'Realm', '~> 3.17.3'

3、初始化配置

3.1、初始化RLMRealmConfiguration

RLMRealmConfiguration包含创建Realm数据库所需要的一切,你应当为每个Realm数据库创建一个RLMRealmConfiguration对象,缓存并复用它,因为设置objectClasses会有一定的开销。

下面是RLMRealmConfiguration类介绍

typedef BOOL (^RLMShouldCompactOnLaunchBlock)(NSUInteger totalBytes, NSUInteger bytesUsed);

@interface RLMRealmConfiguration : NSObject

// 默认Realm配置,设置默认配置后你可以通过 [RLMRealm defaultRealm]很方便的获取Realm对象

+ (instancetype)defaultConfiguration;

+ (void)setDefaultConfiguration:(RLMRealmConfiguration *)configuration;

// Realm本地文件url,即数据库路径

@property (nonatomic, copy, nullable) NSURL *fileURL;

// Realm内存数据库的标志,如果仅需要使用内存缓存可以配置此参数(fileURL将会无效)

@property (nonatomic, copy, nullable) NSString *inMemoryIdentifier;

// 用于加密数据的64字节密钥

@property (nonatomic, copy, nullable) NSData *encryptionKey;

// 只读数据库,如果只需要读取Realm文件不需要写入数据则可以设置为YES

@property (nonatomic) BOOL readOnly;

// 数据库版本号

@property (nonatomic) uint64_t schemaVersion;

// 你需要配置此迁移block来配合数据库升级

@property (nonatomic, copy, nullable) RLMMigrationBlock migrationBlock;

// 如果设置为YES,当存储的版本和提供的版本不一致将会删除并重建Realm数据库文件

@property (nonatomic) BOOL deleteRealmIfMigrationNeeded;

// 压缩数据库

@property (nonatomic, copy, nullable) RLMShouldCompactOnLaunchBlock shouldCompactOnLaunch;

// 指定Realm管理的类

@property (nonatomic, copy, nullable) NSArray *objectClasses;

@end

RLMRealmConfiguration 支持NSCoding协议,使用时应该注意一下几点:

+ (instancetype)defaultConfiguration;接口返回的是s_defaultConfiguration全局变量的copy实例,并非s_defaultConfiguration对象本身,第一次调用会初始化s_defaultConfiguration实例,这种设计可以在多线程之间自由共享和修改

使用+ (void)setDefaultConfiguration:(RLMRealmConfiguration *)configuration;接口可以设置一个默认的config实例,如果需要同时操作多个数据库,多个config是需要的,你可以把最常用的一个设置为默认。

3.2、获取Realm对象

Realm对象负责数据的写入、更新或删除等操作,你可以通过以下接口获取:

// 使用config初始化一个realm对象

+ (nullable instancetype)realmWithConfiguration:(RLMRealmConfiguration *)configuration error:(NSError **)error;

通常情况下你不应该缓存Realm对象,可以直接调用realmWithConfiguration:error:接口获取,内部已经做了缓存处理。

下面是两种更简洁的方式获取Realm对象

// 如果你设置了`DefaultConfiguration`,调用此接口获取Realm对象是一种很简洁的方式

+ (instancetype)defaultRealm {

return [RLMRealm realmWithConfiguration:[RLMRealmConfiguration rawDefaultConfiguration] error:nil];

}

// 此接口会创建或者获取一个默认的config对象,并修改fileURL

+ (instancetype)realmWithURL:(NSURL *)fileURL {

RLMRealmConfiguration *configuration = [RLMRealmConfiguration defaultConfiguration];

configuration.fileURL = fileURL;

return [RLMRealm realmWithConfiguration:configuration error:nil];

}

Realm对象创建依赖于RLMRealmConfiguration实例,每次使用都返回一个Realm对象,此对象可能是重新分配也可能来自于当前的Realm对象缓存。

如果你通过+[RLMRealmConfiguration setDefaultConfiguration:]设置了一个默认的Realm,则可以使用+ (instancetype)defaultRealm来获取默认Realm对象,

+ (instancetype)realmWithURL:(NSURL *)fileURL接口也是使用默认config创建的Realm对象但是修改了config的fileURL属性。

对于多个config实例,你应该使用realmWithConfiguration:error:接口来获取指定的Realm对象进行数据写入。

3.3、多账号配置

假设我们的APP需要登录注销切换,我们需要为每个用户配置一个Realm数据库:

3.3.1、对RLMRealmConfiguration添加扩展:

static NSString *const rlm_remoteUserRealmFileName = @"remoteUser.realm";

RLMRealmConfiguration *rlm_remoteUserConfiguration;

@implementation RLMRealmConfiguration (MyRealmConfig)

+ (RLMRealmConfiguration *)remoteUserConfiguration {

RLMRealmConfiguration *configuration;

@synchronized(rlm_remoteUserRealmFileName) {

if (!rlm_remoteUserConfiguration) {

rlm_remoteUserConfiguration = [[RLMRealmConfiguration alloc] init];

}

configuration = rlm_remoteUserConfiguration;

}

return configuration;

}

#pragma mark - ObjectClass

+ (NSArray*)getObjectClassForRemoteUser

{

return @[[MyObject class]];

}

// 得到指定用户账号的数据库配置

+ (RLMRealmConfiguration*)remoteUserRealmConfiguration:(NSString *)user

{

RLMRealmConfiguration *config = [RLMRealmConfiguration remoteUserConfiguration];

NSArray *libraryPath = NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, YES);

NSString *dataBasePath = [libraryPath objectAtIndex:0];

NSString *filePath = [dataBasePath stringByAppendingPathComponent:user];

config.fileURL = [[NSURL URLWithString:filePath]URLByAppendingPathExtension:@"realm"];

config.objectClasses = [self getObjectClassForRemoteUser];

return config;

}

3.3.2、添加Realm分类

@interface RLMRealm (MyRealm)

+ (void)setDefaultRealmWithUser:(NSString*)userId;

@end

@implementation RLMRealm (MyRealm)

+ (void)setDefaultRealmWithUser:(NSString*)userId

{

if (!userId) {

return;

}

RLMRealmConfiguration *config = [RLMRealmConfiguration remoteUserRealmConfiguration:userId];

config.schemaVersion = 5;

config.migrationBlock = ^(RLMMigration * _Nonnull migration, uint64_t oldSchemaVersion) {

// 升级处理

};

config.shouldCompactOnLaunch = ^BOOL(NSUInteger totalBytes, NSUInteger usedBytes) {

// 压缩策略

NSUInteger oneHundredMB = 100 * 1024 * 1024;

return (totalBytes > oneHundredMB) && ((double)usedBytes / totalBytes) < 0.5;

};

[RLMRealmConfiguration setDefaultConfiguration:config];

[RLMRealm defaultRealm];

}

@end

如需配置一个无账号数据库混合使用,设置一个RLMRealmConfiguration *rlm_localUserConfiguration;全局实例初始化Realm是必要的。

3.3.3、使用Realm对象

// 优先配置好数据库文件

[RLMRealm setDefaultRealmWithUser:@"用户id"];

// 使用默认Realm写入、更新或删除

NSError *error;

RLMRealm *defaultRealm = [RLMRealm defaultRealm];

BOOL ret = [localUserRealm transactionWithBlock:^{

// 存储逻辑Code

} error:&error];

4、对象模型

Realm对象模型对代码有较强的侵入性,所有的可存储对象都需继承RLMObject, 对象存储属性的getter和setter方法将被重写,任何对存储属性的setter和getter修改都将被忽略。

修改一下官方的代码实例:

#import

@class Person;

// Dog model

@interface Dog : RLMObject

@property NSString *name;

@property Person *owner;

@end

RLM_ARRAY_TYPE(Dog) // define RLMArray

// Implementations

@implementation Dog

@end // none needed

// Person model

@interface Person : RLMObject

@property NSString *personId;

@property int age;

@property int sex;

@property NSString *desc;

@property NSString *address;

@property (readonly) NSString *name; // read-only properties are automatically ignored

@property NSString *firstName;

@property NSString *lastName;

@property NSDate *birthdate;

@property RLMArray *dogs;

@end

RLM_ARRAY_TYPE(Person) // define RLMArray

@implementation Person

+ (NSString *)primaryKey {

return @"personId";

}

// 添加索引

+ (NSArray *)indexedProperties {

return @[@"address"];

}

// 默认值

+ (NSDictionary *)defaultPropertyValues {

return @{@"age" : @20, @"sex": @(0)};

}

// 忽略desc属性

+ (NSArray *)ignoredProperties {

return @[@"desc"];

}

// 只读的数据将会被忽略

- (NSString *)name {

return [NSString stringWithFormat:@"%@ %@", self.firstName, self.lastName];

}

@end // none needed

使用Realm对象需要注意以下问题:

RLMArray是集合属性,这里的RLMArray *dogs保持对Dog对象实例的持有,假设用户新添加了一个宠物Dog,你需要先存储Dog对象然后将Dog加入dogs中,如果反复加入,则会保持多份持有。没有特别的需要,你应该确保dogs中持有一个Dog对象,避免重复。

+primaryKey可以设置模型的主键,声明主键可以有效地查找和更新对象,并为每个值强制实现唯一性。将具有主键的对象添加到Realm后,无法更改主键。Realm不支持联合主键,也不支持自增序列。

+indexedProperties。与主键一样,索引使写入速度稍慢,但可以极大的提高查询速度(它还会使您的Realm文件略大,以存储索引)。最好只在特定优化读取性能的情况下添加索引。 Realm支持对字符串,整数,布尔值和NSDate属性进行索引。如果需要频繁写入大量数据,你可以忽略索引。

Realm,请覆盖+ignoredProperties。Realm不会干扰这些属性的正常运行; 他们将得到ivars的支持,你可以自由地覆盖重写他们的getter和setter方法。忽略的属性与普通属性完全相同。它们不支持任何特定于Realm的功能(例如,它们不能在查询中使用,也不会触发通知)。仍然可以使用KVO观察它们。

5、Realm数据写入、更新、删除

Realm的所有写入、更新和删除操作都需要在事务中进行

5.1、插入记录

Dog *myDog = [[Dog alloc] init];

myDog.name = @"Fido";

myDog.age = 1;

NSError *error;

RLMRealm *realm = [RLMRealm defaultRealm];

BOOL ret = [realm transactionWithBlock:^{

[realm addObject:myDog];

} error:&error];

if (ret)

{

// 添加成功

}

如果设置了主键,可以使用addOrUpdateObject接口添加记录,记录存在则会更新覆盖所有属性。

5.2、更新记录

5.2.1、 更新某一个属性

对于已存在的记录,在Realm事务中修改其属性值将会直接修改掉其在磁盘上的存储结果

Dog *myPuppy = [[Dog allObjects] firstObject];

[realm transactionWithBlock:^{

myPuppy.age = 2;

}];

5.2.2、 key-value更新

RLMResults *persons = [Person allObjects];

[[RLMRealm defaultRealm] transactionWithBlock:^{

// 更新第一个人的名字为Sam

[[persons firstObject] setValue:@"Sam" forKeyPath:@"name"];

// 将记录中所有人的名字设置为Sam

[persons setValue:@"Sam" forKeyPath:@"name"];

}];

5.2.3、 通过主键更新

覆盖更新会替换所有的属性值,谨慎使用

Person *person = [[Person alloc] init];

person.name = @"Jack";

person.address = @"ShenZhen";

person.personId = @"ABCDEF";

[realm beginWriteTransaction];

[realm addOrUpdateObject:person];

[realm commitWriteTransaction];

对已存在的记录做部分属性值修改

[realm beginWriteTransaction];

[Person createOrUpdateModifiedInRealm:realm withValue:@{@"personId": @"ABCDEF", @"name": @"Sam"}];

[realm commitWriteTransaction];

5.3、删除记录

删除操作会使所有被缓存的RLMObject记录失效,如果你缓存了一个RLMObject,然后执行了删除操作,但在删除之前没有移除这个对象,则会引发程序Crash,后面我们将会给出解决方案。

5.3.1、删除一个记录

RLMRealm *realm = [RLMRealm defaultRealm];

[realm beginWriteTransaction];

[realm deleteObject:person];

[realm commitWriteTransaction];

5.3.2、删除部分记录

RLMRealm *realm = [RLMRealm defaultRealm];

RLMResults *results = [Person objectsWhere:@"查询条件"];

[realm beginWriteTransaction];

[realm deleteObjects:results];

[realm commitWriteTransaction];

5.3.3、删除所有记录

删除全部记录会删除所有表的数据,请谨慎使用

RLMRealm *realm = [RLMRealm defaultRealm];

[realm beginWriteTransaction];

[realm deleteAllObjects];

[realm commitWriteTransaction];

6、Realm通知

Realm允许你添加通知,你可以对对象和集合添加通知,当Realm被更新或者对象插入、删除和修改等都会收到对应的通知。

6.1、Realm对象通知

注册了Realm通知,每次提交涉及该Realm的写入事务时,无论写入事务发生在哪个线程或进程上,都将触发通知处理程序:

RLMRealm *realm = [RLMRealm defaultRealm];

RLMNotificationToken *token = [realm addNotificationBlock:^(NSString *notification, RLMRealm * realm) {

[myViewController updateUI];

}];

// 强引用Realm Token,确保通知正常接收

self.token = token;

// 在不用的时候释放token,注销通知

[self.token invalidate];

6.2、集合通知

注册集合通知不会收到整个Realm的通知,而是收到此集合的详细更改说明。它们包括自上次通知以来已添加、删除或修改的对象索引。集合通知是异步传递的,首先收到初始结果通知,之后每次事务带来的集合对象的任何改变都会再次收到通知。

__weak typeof(self) weakSelf = self;

id collection = [Person objectsWhere:@"查询条件"];

self.notificationToken =

[collection addNotificationBlock:^(RLMResults *results, RLMCollectionChange *changes, NSError *error) {

if (error) {

NSLog(@"Failed to open Realm on background worker: %@", error);

return;

}

UITableView *tableView = weakSelf.tableView;

// Initial run of the query will pass nil for the change information

if (!changes) {

[tableView reloadData];

return;

}

// Query results have changed, so apply them to the UITableView

[tableView beginUpdates];

[tableView deleteRowsAtIndexPaths:[changes deletionsInSection:0]

withRowAnimation:UITableViewRowAnimationAutomatic];

[tableView insertRowsAtIndexPaths:[changes insertionsInSection:0]

withRowAnimation:UITableViewRowAnimationAutomatic];

[tableView reloadRowsAtIndexPaths:[changes modificationsInSection:0]

withRowAnimation:UITableViewRowAnimationAutomatic];

[tableView endUpdates];

}];

6.3、RLMObject对象通知

Realm支持对象级通知。您可以在特定Realm对象上注册通知,以便在删除对象时或在对象上的任何托管属性修改其值时收到通知。

Person *aPerson = [[Person allObjects]firstObject];

__block RLMNotificationToken *token = [aPerson addNotificationBlock:^(BOOL deleted,

NSArray *changes,

NSError *error) {

if (deleted) {

NSLog(@"The object was deleted.");

}

else if (error) {

NSLog(@"An error occurred: %@", error);

}

else {

for (RLMPropertyChange *property in changes) {

if ([property.name isEqualToString:@"age"] && [property.value integerValue] > 30) {

// 做一些处理

}

}

}

}];

7、Realm查询

Realm查询相当简洁,完美支持OC的谓词查询。查询返回一个id集合即RLMResults实例。如果在事务中对查询的结果进行修改将会直接改变磁盘上的数据。

7.1、查询所有数据

RLMResults *persons = [Person allObjects];

查询将会返回一个RLMResults集合对象,支持快速枚举等,只有真正开始访问集合数据时,磁盘数据才载入到内存,使用完毕立即释放,即用即取特性极大减小了内存占用,但频繁读取也增加了IO开销

7.2、条件查询

Realm提供了丰富的条件查询接口,你可以根据需要选择合适的查询方式

// 查询中年龄大于20,姓名以`S`开头的所有person

RLMResults *persons = [Person objectsWhere:@"age > 2o AND name BEGINSWITH 'S'"];

// 使用 NSPredicate查询

NSPredicate *predicate = [NSPredicate predicateWithFormat:@"age > %@ AND name BEGINSWITH %@",

@(20), @"B"];

persons = [Person objectsWithPredicate:predicate];

7.3、链式查询

RLMResults集合支持对象查询操作,你可以对查询的结果进行再次过滤

RLMResults *tanDogs = [Dog objectsWhere:@"color = 'tan'"];

RLMResults *tanDogsWithBNames = [tanDogs objectsWhere:@"name BEGINSWITH 'B'"];

7.4、结果排序

// 查询所有的Dog,并按照name升序排序

RLMResults *sortedDogs = [[Dog objectsWhere:@"color = 'tan' AND name BEGINSWITH 'B'"]

sortedResultsUsingKeyPath:@"name" ascending:YES];

// 查询所有的Dog Owners 并按照Dog的年龄对主人排序

RLMResults *dogOwners = [Person allObjects];

RLMResults *ownersByDogAge = [dogOwners sortedResultsUsingKeyPath:@"dog.age" ascending:YES];

7.5、限量读取

当数据库记录过多时可以分批读取数据,RLMResults集合提供了按索引查找、谓词查询、条件排序等能力,并未提供合适的分页方案。后面我们会介绍到如何巧妙的使用Realm集合进行分页。

// 只有前5条数据被读入到内存

RLMResults *dogs = [Dog allObjects];

for (NSInteger i = 0; i < 5; i++) {

Dog *dog = dogs[i];

// ...

}

android realm 分页,iOS Realm数据持久化--Realm基础知识 (一)相关推荐

  1. iOS8开发视频教程Swift语言版-Part 10:iOS的数据持久化-关东升-专题视频课程

    iOS8开发视频教程Swift语言版-Part 10:iOS的数据持久化-17164人已学习 课程介绍         本课程主要介绍了iOS数据持久化的方式,沙箱目录,以及属性列表和对象归档,并且重 ...

  2. Part 10:iOS的数据持久化(1),文件,归档

    本章中主要学习数据持久化与各种持久化方式的实现方法 沙箱目录 沙箱目录设计的原理就是只能允许自己的应用访问目录,而不允许其他的应用访问. 1. Documents 目录 大量的数据,经常变化,最重要的 ...

  3. IOS学习——数据持久化(二):初识CoreData

    请配合 姊妹篇 食用效果更佳!!! 昨天被问到 SQLite3与Core Data有什么区别,当时只是想到了一个是充分利用了传统的SQL语句,另一个是充分利用了面向对象的编程方法,今天早上想了一会,决 ...

  4. Android音视频学习系列(五) — 掌握音频基础知识并使用AudioTrack、OpenSL ES渲染PCM数据

    系列文章 Android音视频学习系列(一) - JNI从入门到精通 Android音视频学习系列(二) - 交叉编译动态库.静态库的入门 Android音视频学习系列(三) - Shell脚本入门 ...

  5. Android音视频学习系列(六) — 掌握视频基础知识并使用OpenGL ES 2.0渲染YUV数据

    系列文章 Android音视频学习系列(一) - JNI从入门到精通 Android音视频学习系列(二) - 交叉编译动态库.静态库的入门 Android音视频学习系列(三) - Shell脚本入门 ...

  6. IOS Socket 01-网络协议基础知识

    IOS Socket 01-网络协议基础知识 1. 网络参考模型 OSI参考模型                                          TCP/IP参考模型 2. 七层简述 ...

  7. 数据可视化的基础知识·翻译完成

    原文:Fundamentals of Data Visualization 校验:飞龙 协议:CC BY-NC-SA 4.0 欢迎任何人参与和完善:一个人可以走的很快,但是一群人却可以走的更远. Ap ...

  8. 数据可视化系列-04数据大屏基础知识

    文章目录 5.销售数据看板 5.1 了解数据大屏基础知识 1.数据大屏简介: 2.数据大屏使用场景 3.数据大屏分类 5.2 数据大屏的设计: 1.大屏前端设计流程 2.数据大屏设计尺寸解析 3.可视 ...

  9. iOS应用数据持久化(一)

    一.iOS应用常用的数据持久化方式: 1.XML属性列表(plist) 2.preference(偏好设置) 3.NSKeyedArchiver(归档与反归档) 4.SQLite3(数据库) 5.Co ...

最新文章

  1. vim中如何按一个键就保存文件
  2. Linux简单的颜色设置
  3. windows 10占用cpu和内存过高
  4. Top 10 Things I Know for Sure-深信不疑的十大哲理
  5. [转载] ANTLR——编译原理基础知识
  6. 下列选项中不属于python循环语句的是哪一项_下列选项中,不属于字典操作的方法是哪一项?_学小易找答案...
  7. python风控工具_python-风控模型分析01
  8. 什么是机器学习?有哪些分类?到底有什么用?终于有人讲明白了
  9. 清空文件夹,或删除文件夹
  10. JQuery EasyUI框架
  11. 用JavaScript替换重复字符
  12. linux虚拟机中如何复制粘贴内容到主机
  13. Pos应用与扫盲一路通
  14. 创业者应警惕可疑的成功
  15. android 动态毛玻璃,Android毛玻璃背景效果简单实现代码
  16. APP推广渠道下载量统计方案
  17. css实现跑马灯效果
  18. 景观平面图转鸟瞰图_快题干货| 速码!鸟瞰图、效果图、扩初图…5大辅助小图考试技巧全面大放送!...
  19. Linux wait和waitpid和kill
  20. Lab颜色空间及其应用

热门文章

  1. IE与FireFox,速度和兼容性水平相当
  2. Java中CountDownLatch类
  3. Java并发编程之AQS以及源码解析
  4. crt 安卓系统的secure_SecureCRT
  5. 【AI达人创造营三期】Jetson Nano篮球和运动员检测分割的部署
  6. 微服务编排 conductor_智能家居巨头 Aqara 基于 KubeSphere 打造物联网微服务平台
  7. 豪情哥的忠告 能做到这一条就够用了
  8. 使用gdb调试程序完全教程
  9. NO.16 沙场秋点兵:类vs抽象类vs接口 | Java敲黑板系列
  10. 第四届字节跳动青训营