IOS知识积累Ream数据库数据库

Realm在iOS中的简单使用

2018-08-10  本文已影响505人  TitanCoder

一、Realm简单介绍

1、Realm简介

2、Realm的优势与亮点

3、可视化工具Realm Browser

为了配合Realm的使用,Realm还提供了一个轻量级的数据库查看工具Realm Browser,借助这个工具,开发者可以查看数据库当中的内容,并执行简单的插入和删除操作。Realm Browser可以在App Store中下载安装

[图片上传失败...(image-b6adbf-1533872285982)]

<div class="note warning"><p>需要注意的是</p></div>

如果需要调试, 可以通过NSHomeDirectory()打印出Realm数据库地址, 找到对应的Realm文件, 然后用Realm Browser可视化工具打开即可

二、Realm的安装

Realm的Github地址

1、手动安装

当使用手工方式安装Realm时,可以按照如下步骤进行

RealmResource

2、使用CocoaPods安装

当使用CocoaPods方式安装Realm时,以Objective-C为例

<div class="note info"><p>RLMSupport.swift</p></div>

RLMSupport.swift这个文件为Objective-C版本的Realm集合类型中引入了Sequence一致性,并且重新暴露了一些不能够从Swift中进行原生访问的Objective-C方法,例如可变参数variadic arguments等, 更加详细的说明见官方文档

3、Xcode插件

Realm Model

三、Realm的类定义说明

Realm框架中,定义了二十个核心类、常量、枚举类型、协议等,常用的如:RLMRealm类、RLMObject类、RLMResults类等, 我们可以从Realm官方网站上查看所有的定义以及使用说明

1、RLMRealm类

// 获取默认的Realm数据库
+ (instancetype)defaultRealm;


//实例化一个RLMRealm类的对象
//根据配置参数获得RLMRealm
+ (nullable instancetype)realmWithConfiguration:(RLMRealmConfiguration *)configuration error:(NSError **)error;

//根据指定持久化文件路径获得RLMRealm
+ (instancetype)realmWithPath:(NSString *)path;


//对Realm数据库进行读写操作
//在Realm上开始写入事务, 每个Realm文件一次只能打开一个写事务
- (void)beginWriteTransaction;

//在当前写入事务中提交所有写入操作,并结束事务
- (void)commitWriteTransaction;
//没有足够的磁盘空间来保存写入或由于意外的I / O错误,此方法可能会失败, 并返回error信息
- (BOOL)commitWriteTransaction:(NSError **)error;
// 在当前写入事务中提交所有写入操作,而不收到此写入事件的特定通知
- (BOOL)commitWriteTransactionWithoutNotifying:(NSArray<RLMNotificationToken *> *)tokens error:(NSError **)error;

// 回滚在当前写入事务期间进行的所有写入并结束事务
- (void)cancelWriteTransaction;

//执行写入事务内给定块中包含的操作
- (void)transactionWithBlock:(__attribute__((noescape)) void(^)(void))block;
//执行写入事务内给定块中包含的操作, 如果发生错误,则返回时包含NSError描述问题的对象
- (BOOL)transactionWithBlock:(__attribute__((noescape)) void(^)(void))block error:(NSError **)error;


//添加或更新一个对象
- (void)addObject:(RLMObject *)object;
//将现有对象添加或更新到Realm中, 有则更新没有则插入
- (void)addOrUpdateObject:(RLMObject *)object;
//添加或更新多个对象
- (void)addObjects:(id<NSFastEnumeration>)objects;
- (void)addOrUpdateObjects:(id<NSFastEnumeration>)objects;


//删除对象
- (void)deleteObject:(RLMObject *)object;
- (void)deleteObjects:(id)array;
- (void)deleteAllObjects;

2、RLMObject类

//创建Realm对象, 传入一个NSArray或NSDictionary实例来设置对象属性的值
- (nonnull instancetype)initWithValue:(nonnull id)value;

//在Realm数据库中,获取该RLMObject类的所有对象
+ (RLMResults *)allObjects;

//根据查询条件返回满足条件的所有RLMObject类的对象
+ (RLMResults *)objectsWhere:(NSString *)predicateFormat, ...;

//使用默认Realm中的给定主键检索此对象类型的单个实例
+ (nullable instancetype)objectForPrimaryKey:(nullable id)primaryKey;

//从指定的Realm返回此对象类型的所有对象
+ (nonnull RLMResults *)allObjectsInRealm:(nonnull RLMRealm *)realm;

//返回与指定Realm中给定谓词匹配的此对象类型的所有对象
+ (nonnull RLMResults *)objectsInRealm:(nonnull RLMRealm *)realm where:(nonnull NSString *)predicateFormat, ...;

3、RLMResults类

<div class="note info"><p>相关属性</p></div>

//结果集合中的对象个数
@property (readonly, assign, nonatomic) NSUInteger count;

//结果集合中对象的类型
@property (readonly, assign, nonatomic) RLMPropertyType type;

//管理此结果集合的Realm对象
@property (readonly, nonatomic) RLMRealm *_Nonnull realm;

//结果集合中包含的对象的类名称
@property (readonly, copy, nonatomic, nullable) NSString *objectClassName;

<div class="note info"><p>相关方法</p></div>

//返回结果集合中的第一个对象
- (nullable RLMObjectType)firstObject;

//返回结果集合中的最后一个对象
- (nullable RLMObjectType)lastObject;

//根据索引index获取其中的某个对象
- (RLMObjectType)objectAtIndex:(NSUInteger)index;

//根据对象返回其索引
- (NSUInteger)indexOfObject:(RLMObjectArgument)object;

//返回与谓词匹配的结果集合中第一个对象的索引
- (NSUInteger)indexOfObjectWhere:(nonnull NSString *)predicateFormat, ...;

//返回与结果集合中给定谓词匹配的所有对象
- (RLMResults<RLMObjectType> *)objectsWhere:(NSString *)predicateFormat, ...;

//返回RLMResults从现有结果集合中排序的内容
- (RLMResults<RLMObjectType> *)sortedResultsUsingKeyPath:(NSString *)keyPath ascending:(BOOL)ascending;

//返回RLMResults与现有结果集合不同的内容
- (nonnull RLMResults<RLMObjectType> *)distinctResultsUsingKeyPaths:(nonnull NSArray<NSString *> *)keyPaths;

<div class="note warning"><p>更多相关类及其属性和方法, 可参考官方文档</p></div>

https://realm.io/docs/objc/latest/api/Classes.html

四、Realm的使用

Realm中一些常用的类及其类的属性和方法上面已经介绍了, 下面我们就介绍Realm的使用方法

1、创建RLMObject类

我们首先创建一个Student类,该类是RLMObject类的一个子类, 下图就是按照之前安装的Xcode插件创建的

image
@interface Student : RLMObject

@property int num;
@property NSString *name;

@end
RLM_ARRAY_TYPE(Student)

2、存储操作

// 方式一: 接受一个数组对象
Student *stu1 = [[Student alloc]initWithValue:@[@1, @"jun"]];

//方式二: 接受一个字典对象
Student *stu2 = [[Student alloc]initWithValue:@{@"num": @2, @"name":@"titan"}];

//方式三: 属性赋值
Student *stu3 = [[Student alloc]init];
stu3.num = 3;
stu3.name = @"titanjun";

第二步就是把RLMObject对象写入Realm数据库, 同样有三种方式

//方式一: 提交事务处理
//获取Realm对象
RLMRealm *realm = [RLMRealm defaultRealm];
//开始写入事务
[realm beginWriteTransaction];
//添加模型对象
[realm addObject:stu1];
//提交写入事务
[realm commitWriteTransaction];


//方式二: 在事务中调用addObject:方法
RLMRealm *realm = [RLMRealm defaultRealm];
[realm transactionWithBlock:^{
    [realm addObject:webSite1];
    [realm addObject:webSite2];
}];


//方式三: 在十五中创建新的对象并添加
[realm transactionWithBlock:^{
    //添加模型
    [Student createInRealm:realm withValue:@{@"num": @3, @"name":@"coder"}];
}];

<div class="note warning"><p>一定要注意的是</p></div>

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        
    RLMRealm *realm = [RLMRealm defaultRealm];
    [realm transactionWithBlock:^{
        [realm addObject: stu4];
    }];
});

3、查询操作

全量查询

//1. 获取所有数据
RLMResults *resArr = [Student allObjects];
NSLog(@"%@", resArr);

//2. 添加一条数据
RLMRealm *realm = [RLMRealm defaultRealm];
Student *stu = [[Student alloc]initWithValue:@[@10, @"coder"]];
[realm transactionWithBlock:^{
    [realm addObject:stu];
}];

//3. 一旦检索执行之后, RLMResults 将随时保持更新
NSLog(@"%@", resArr);

条件查询

//条件查询
RLMResults *stuArr = [Student objectsWhere:@"num > 7"];
NSLog(@"%@", stuArr);

//排序
//排序不会对原数组进行操作, 会返回一个新的数组
RLMResults *stuArr2 = [stuArr sortedResultsUsingKeyPath:@"name" ascending:YES];
NSLog(@"%@", stuArr2);


//链式查询
RLMResults *stuArr3 = [stuArr2 objectsWhere:@"num > 8"];
//可以不断的根据上一个查询结果进行查询
RLMResults *stuArr4 = [stuArr3 objectsWhere:@"num > 9"];
NSLog(@"%@", stuArr4);

4、更新操作

//获取Realm对象
RLMRealm *realm = [RLMRealm defaultRealm];
Student *stu4 = [[Student alloc]initWithValue:@{@"num": @4, @"name":@"titan4"}];
//添加数据
// 这个模型stu, 已经被realm 所管理, 而且, 已经和磁盘上的对象, 进行的地址映射
[realm transactionWithBlock:^{
    //添加模型
    [realm addObject:stu4];
}];

// 这里修改的模型, 一定是被realm所管理的模型
[realm transactionWithBlock:^{
    stu4.name = @"coder4";
}];
//条件查询
RLMResults *results = [Student objectsWhere:@"num = 4"];
Student *stu = results.firstObject;

//更新指定属性的数据
[realm transactionWithBlock:^{
    stu.name = @"titanking";
}];
//获取Realm对象
RLMRealm *realm = [RLMRealm defaultRealm];
Student *stu2 = [[Student alloc]initWithValue:@{@"num": @12, @"name":@"titan"}];

//addOrUpdateObject方式
[realm transactionWithBlock:^{
    [realm addOrUpdateObject:stu2];
}];

//createOrUpdateInRealm方式
[realm transactionWithBlock:^{
    [Student createOrUpdateInRealm:realm withValue:@{@"num": @11, @"name":@"titan11"}];
}];

5、删除操作

//获取Realm对象
RLMRealm *realm = [RLMRealm defaultRealm];

//根据条件删除一条数据 
RLMResults *results = [Student objectsWhere:@"name = 'titanking'"];
Student *titan1 = results.firstObject;

// 删除单条记录
[realm transactionWithBlock:^{
    [realm deleteObject:titan1];
}];



//删除所有符合条件的数据
RLMResults *results = [Student objectsWhere:@"name = 'coder'"];
for (Student *stu in results) {
    [realm transactionWithBlock:^{
        [realm deleteObject:stu];
    }];
}



//删除表中所有的数据
[realm transactionWithBlock:^{
    [realm deleteAllObjects];
}];



/*场景, 根据主键删除一个模型*/
 // 1. 根据主键, 查询到这个模型(这个模型, 就是被realm数据库管理的模型)
Student *res = [Student objectInRealm:realm forPrimaryKey:@4];

//2. 删除该模型
[realm transactionWithBlock:^{
    [realm deleteObject:res];
}];

6、Realm数据库机制

- (void)setDefaultRealmForUser:(NSString *)username {
    //先获取默认配置
    RLMRealmConfiguration *config = [RLMRealmConfiguration defaultConfiguration];
    
    //设置只读数据库
    //config.readOnly = YES;
    
    // 使用默认的目录,但是使用用户名来替换默认的文件名
    config.fileURL = [[[config.fileURL URLByDeletingLastPathComponent]
                       URLByAppendingPathComponent:username]
                      URLByAppendingPathExtension:@"realm"];
    // 将这个配置应用到默认的 Realm 数据库当中
    [RLMRealmConfiguration setDefaultConfiguration:config];
}

做好上述配置之后, 便可创建不同的数据库了

// 不同的用户, 使用不同的数据库
[self setDefaultRealmForUser:@"zhangsan"];

//这里也只需要调用默认配置即可
RLMRealm *realm = [RLMRealm defaultRealm];

7、通知

//强引用属性
@property (nonatomic, strong) RLMNotificationToken *token;


- (void)setUp {
    [super setUp];

    RLMRealm *realm = [RLMRealm defaultRealm];
    // 获取 Realm 通知
    self.token = [realm addNotificationBlock:^(RLMNotification  _Nonnull notification, RLMRealm * _Nonnull realm) {
        NSLog(@"接收到变更通知--%@", notification);
    }];

    //结束该通知
    [self.token stop];
}


- (void)testExample {
    NoticeModel *noticeM = [[NoticeModel alloc] initWithValue:@{@"num": @1, @"name": @"sz"}];

    //添加数据, 数据操作之后便会通知上述通知中心执行相应操作
    RLMRealm *realm = [RLMRealm defaultRealm];
    [realm transactionWithBlock:^{
        [realm addObject:noticeM];
    }];
}

8、数据库迁移

//需要在以下方法中进行配置
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    // 获取默认配置, 迁移数据结构
    RLMRealmConfiguration *config = [RLMRealmConfiguration defaultConfiguration];

    // 1. 设置新的架构版本。这个版本号必须高于之前所用的版本号(如果您之前从未设置过架构版本,那么这个版本号设置为 0)
    int newVersion = 4;
    config.schemaVersion = newVersion;

    // 2. 设置闭包,这个闭包将会在打开低于上面所设置版本号的 Realm 数据库的时候被自动调用
    [config setMigrationBlock:^(RLMMigration *migration, uint64_t oldSchemaVersion){
        if (oldSchemaVersion < newVersion) {

            NSLog(@"数据结构会自动迁移");

            // enumerateObjects:block: 遍历了存储在 Realm 文件中的每一个“Person”对象
            [migration enumerateObjects:@"DataMigration" block:^(RLMObject * _Nullable oldObject, RLMObject * _Nullable newObject) {
                // 只有当 Realm 数据库的架构版本为 0 的时候,才添加 “fullName” 属性
                if (oldSchemaVersion < 1) {
                    newObject[@"fullName"] = [NSString stringWithFormat:@"%@ %@", oldObject[@"firstName"], oldObject[@"lastName"]];
                }
                // 只有当 Realm 数据库的架构版本为 0 或者 1 的时候,才添加“email”属性
                if (oldSchemaVersion < 2) {
                    newObject[@"email"] = @"";
                }
                // 替换属性名(原字段重命名)
                if (oldSchemaVersion < 3) { // 重命名操作应该在调用 `enumerateObjects:` 之外完成
                    [migration renamePropertyForClass:Person.className oldName:@"yearsSinceBirth" newName:@"age"];
                }
            }];
        }
    }];

    // 3. 告诉 Realm 为默认的 Realm 数据库使用这个新的配置对象
    [RLMRealmConfiguration setDefaultConfiguration:config];

    // 4. 现在我们已经告诉了 Realm 如何处理架构的变化,打开文件之后将会自动执行迁移
    [RLMRealm defaultRealm];
    
    
    return YES;
}
上一篇 下一篇

猜你喜欢

热点阅读