realm 实战笔记

一、Realm 框架

官网地址
介绍:
(1)、realm 是一个跨库平台的移动端数据库引擎,支持 iOS、OS X (objective-C和swift) 以及android
(2)、核心数据库引擎C++ 打造,并不是建立在sqlite上的ORM,是拥有独立的数据库引擎.
(3)、性能,据说比sqlite 和coredata要快
(4)、易用性,相比sqlite、coredata,使用起来更加简单,更易入门.

二、使用教程

使用教程

三、辅助工具

  • 1、Realm Browser --> 可是化的访问Realm 数据库的工具,直接在 app store 搜索realm 即可下载.


    Snip20180811_1.png
  • 2、Xcode 插件 下载后直接运行即可,成功后打开Xcode 可看见如下图标,即成功了.
    Snip20180811_2.png

四、Xcode导入realm 框架

  • 创建podfile 安装realm
platform :ios, '9.0'
target 'realm Demo' do
  use_frameworks!
  pod 'Realm', '~> 3.1.1'
end

五、Realm 实战

一、简单的数据操作
  • 1、准备:
    (1) 、创建的数据模型(objective-c 类)必须是继承自RLMObject的对象.
    (2)、由于realm 在自己的引擎内部有很好的语义解释系统,所以objective-c 的许多属性特性将被忽略,如: nonatomic, atomic, strong,copy,和weak等.因此为了避免误解,官方推荐在编写数据模型时不要使用任何的属性特性.
    Snip20180811_4.png

    (3)、所有的必须属性(排除忽略的)在对象添加到realm前,都必须被赋值.
    (4)、 创建对象的方式,可以通过普通方式创建, 也可以使用 RLMObject 中的方法快速创建initWithValue
    Dog *dog =[ [Dog alloc]initWithValue:@{@"name":@"xiaohuang",@"age":@(22)}];
    
    (5) 注意:
    在realm 中只能存储,BOOL, bool,int, NSInteger,long, long long ,float ,double,NSString,NSData,NSDate,NSNumber ,像NSArray NSDictionary 是不能存储的.



  • 2、 使用RLMObject 对象,保存指定的模型:
    (1) 、方式1:
    //1. 获取realm 对象 (可以理解成为数据库的句柄, 对数据库的增删查改都要用到它)
      RLMRealm *realm = [RLMRealm defaultRealm];
      //2. 创建 数据模型对象
      Dog *dog = [[Dog alloc]initWithValue:@{@"name":@"xiaoHuang", @"age":@(2)}];
      //3. 开启写入事物
      [realm beginWriteTransaction];
      //4. 添加数据模型
      [realm addObject:dog];
      //5. 提交写入事物
      [realm commitWriteTransaction];
      
      // 注意: 对realm 数据库的增 删 查 改 的操作都必须写在realm 的事物中来执行,否则报错
    
    (2) 、方式二:
     //1. 获取realm 对象 (可以理解成为数据库的句柄, 对数据库的增删查改都要用到它)
      RLMRealm *realm = [RLMRealm defaultRealm];
        //2. 创建 数据模型对象
      Dog *dog = [[Dog alloc]initWithValue:@{@"name":@"xiaoHuang", @"age":@(2)}];
        // 3.
      [realm transactionWithBlock:^{
          //4. 添加数据模型
          [realm addObject:dog];
      }];
    
    (3)、方式三
    //1. 获取realm 对象 (可以理解成为数据库的句柄, 对数据库的增删查改都要用到它)
      RLMRealm *realm = [RLMRealm defaultRealm];
      //2. bolck 事物
      [realm transactionWithBlock:^{
          //3. 添加数据模型
          [Dog createInRealm:realm withValue:@{@"name":@"xiaoHuang", @"age":@(2)}];
      }];
    
    注意:
    在realm 的事物中没有回滚的概念,如果执行时出错,它内部会自动回滚,外面想要监听操作的异常,就使用下面这个方法,如果错处就会有 error
       NSError *err = nil;
      [realm transactionWithBlock:^{
          //3.增 删 查 改
      }error:&err];
      if(err){
        NSLog(@"realm 在 操作过程中出现了错误: %@",err);
      }
    



  • 3 使用RLMRealm 对象, 更新指定的模型
    (1) 、方式1: 在事物中直接更新对象.
    注意:
    此种方式更新的对象必须是已经和realm 关联的对象,否则无效,如下:

    [realm transactionWithBlock:^{
         //4.注意 在realm 的事物中要 更新的 模型数据必须是 和 realm 已经建立关系的模型数据
         // 否则没办法执行
         dog.age = 20;
     }];
    

    (2) 、方式2: 更具主键去更新(必须要告诉主键)

    [realm transactionWithBlock:^{
          // 注意: 通过这种方式 来添加或者更新某个数据, 模型的类必须实现 +(NSString *)primaryKey; 方法
          [realm addOrUpdateObject:dog];
      }];
    

    (3) 、方式3:

    [realm transactionWithBlock:^{
         // 注意: 通过这种方式 来添加或者更新某个数据, 模型的类必须实现 +(NSString *)primaryKey; 方法
          [Dog createOrUpdateInRealm:realm withValue:@{@"name":@"xiaohei",@"age":@(2)} ];
      }];
    
  • 4、 使用RLMRealm 对象 ,删除数据
    (1)方式1: 删除指定对象

    [realm transactionWithBlock:^{
          // 注意: 要删除的对象必须是和real 相关联的对象, 自定义的没有和realm 关联的对象是不能被删除的
          [realm deleteObject:dog];
      }];
    // 也就是说,如果你要删除某个制定个的数据对象,
    //要先从realm 中把它查找出来,让后在删除.
    

    (2)方式2: 删除所有的对象(有坑)

    // 注意: 这个方法有点猛, 他会删除你所有表的所有的内容, 因此要慎用
    [realm transactionWithBlock:^{
          [realm deleteAllObjects];
      }];
    // 如果只想删除某一张表,或一类数据,那么要分2步.
    // 第一步: 通过realm  查出某一张表的所有的对象
    RLMResults *rsts = [Dog allObjects]; // 这是一个支持快速遍历的集合
    // 第二不: 删除查询出来的所有的数据
    [realm transactionWithBlock:^{
          [realm deleteObjects:rsts];
     }];
    



  • 5、使用RLMRealm 对象查询数据(有坑)
    (1) 、注意事项:
      (1.1) 所有的查询(包括查询和属性访问)在realm 中都是延迟加载的,只有当属性被访问时,才能够读取响应的数据
      (1.2) 查询结果并不是数据的拷贝:修改查询结果(在写入事物中)会直接修改硬盘删的数据.
      (1.3)一旦检索执行后,RLMResults 将随时保持更新.

    RLMRealm *realm = [RLMRealm defaultRealm];
      
      // 查询所有的数据
      RLMResults *rsts = [Dog allObjects];
      NSLog(@"前=========rstsCount:%ld, rsts : %@",rsts.count, rsts);
      
      // 查询完成后再插入一条数据
      [realm transactionWithBlock:^{
          
          [realm addObject:[[Dog alloc]initWithValue:@{@"name":@"xiaoHei",@"age":@(2)}]];
      }];
      
      NSLog(@"后=========rstsCount:%ld, rsts : %@",rsts.count, rsts);
      // rsts 的数据信息会动态的变化而不是固定的, 前后两次打印信息是不同的
    

    (2)、查询所有

    // 所有继承自 RLMObject 的对象都有这个方法
    [Dog allObjects];
    

    (3)、条件查询

    // 查询条件和 sql 语法一样
    RLMResults<Dog *> dogs = [Dog objectsWhere:@"age < 3"];
    

    (4)、排序

    [dogs sortedResultsUsingProperty:@"age" ascending:YES];
    

    (5)、链式查询:
    含义:
    在查询结果的基础上,进行二次查询

    [dogs objectsWhere:@"address begingswith '成都' "];
    

    (6)分页查询 (没有具体的分页,需要自己查出结果集,自己取想要的)


二、 Realm 支持的数据类型
  • 1 、Realm 只支持 BOOL ,bool, NSinteger,long, long long,float,double,NSString, NSDate,NSDate,NSNumber 这几种.
  • 2 、注意: 不支持集合(NSArray,NSDictionary)类型, 但是可以支持RLMArray 这种集合类型
    // RLMArray 是这样定义的
    @interface RLMArray<RLMObjectType> :   NSObject<RLMCollection, NSFastEnumeration>
    // 可以看出, RLMArray 内存储的对象必须是继承自 RLMObject 的对象, RLMArray 相当于Foundation 框架中的NSArray
    
    //在Person 数据模型中使用集合
    //第一步: 定义Dog 数据类型
    @interface Dog : RLMObject
    @property NSString *name;
    @property(nonatomic, assign)int age;
    @end
    //这一行比不可少
    RLM_ARRAY_TYPE(Dog)
    
    //第二步: 定义Person 数据类型
    @interface Person : RLMObject
    @property NSString  *name;
    @property int age;
    //1. 通过以下这种方式在模型数据中 定义 realm集合
    //2. 指定集合中存储的是 Dog 类型
    @property RLMArray<Dog *><Dog> *dogs;
    @end
    



三 、realm 数据之间的关系
  • 1 、对一关系:
    当一个对象持有另一个对象是,这种关系我们就称之为对一关系,比如: 一个person 对象持有一个Dog 对象.
    //Person.h 文件
    #import <Realm/Realm.h>
    #import "Dog.h"
    @interface Person : RLMObject
    @property NSInteger num;
    @property NSString  *name;
    @property int age;
    /** realm 中的一对一的关系 */
    @property Dog *dog;
    @end
    RLM_ARRAY_TYPE(Person)
    
    // Person.m 文件
    #import "Person.h"
    @implementation Person
    +(NSString *)primaryKey{
        return @"num";
    }
    @end
    
    
    //Dog.h 文件
    #import <Realm/Realm.h>
    @interface Dog : RLMObject
    @property NSInteger num;
    @property NSString  *name;
    @property int age;
    @end
    RLM_ARRAY_TYPE(Dog)
    
    // Dog.m 文件
    #import "Dog.h"
    @implementation Dog
    +(NSString *)primaryKey{
        return @"num";
    }
    @end
    
    // 实际方法应用
    - (void)testExample {
        //1. 获取realm 对象 (可以理解成为数据库的句柄, 对数据库的增删查改都要用到它)
        RLMRealm *realm = [RLMRealm defaultRealm];
      
        Person *pson = [[Person alloc] initWithValue:@{@"num":@(1), @"name":@"zhangsan",@"age":@(18)}];
        Dog *dog = [[Dog alloc]initWithValue:@{@"num":@(1), @"name":@"xiaoHei",@"age":@(1)}];
        pson.dog = dog;
        // 3.
        [realm transactionWithBlock:^{
            //4. 添加数据模型
            [realm addObject:pson];
        }];
        NSLog(@"pson: %@",pson);
      }
    
Snip20180811_6.png
  • 2、 对多关系:
    (1)在Dog类中,遵循指定协议方法,RLM_ARRAY_TYPE(Dog), RLM_ARRAY_TYPE 宏创建了一个协议,从而允许RLMArray<Dog* ><Dog> * dogs; 这种语法的使用.
    (2)在Person类中,定义属性 @property RLMArray<Dog* ><Dog>* dogs;
    注意:
    (2.1)、虽然可以个RLMArray 属性赋值为nil,但是这仅用于清空数组,而不是移除数组,这意味着你总是可以向一个RLMArray 属性中添加对象,即使其被值为了nil
    //Person.h 文件
    #import <Realm/Realm.h>
    #import "Dog.h"
    @interface Person : RLMObject
    @property NSInteger num;
    @property NSString  *name;
    @property int age;
    /** realm 中的一对多的关系 */
    @property RLMArray<Dog *><Dog> *dogs;
    @end
    RLM_ARRAY_TYPE(Person)
    
    // Person.m 文件
    #import "Person.h"
    @implementation Person
    +(NSString *)primaryKey{
        return @"num";
    }
    @end
    
    
    //Dog.h 文件
    #import <Realm/Realm.h>
    @interface Dog : RLMObject
    @property NSInteger num;
    @property NSString  *name;
    @property int age;
    @end
    RLM_ARRAY_TYPE(Dog)
    
    // Dog.m 文件
    #import "Dog.h"
    @implementation Dog
    +(NSString *)primaryKey{
        return @"num";
    }
    @end
    
    // 实际方法应用
    - (void)testExample {
        //1. 获取realm 对象 (可以理解成为数据库的句柄, 对数据库的增删查改都要用到它)
        RLMRealm *realm = [RLMRealm defaultRealm];
      
        Person *pson = [[Person alloc] initWithValue:@{@"num":@(1), @"name":@"zhangsan",@"age":@(18)}];
      
        Dog *dog1 = [[Dog alloc]initWithValue:@{@"num":@(1), @"name":@"xiaoHei",@"age":@(1)}];
        Dog *dog2 = [[Dog alloc]initWithValue:@{@"num":@(2), @"name":@"xiaoHuang",@"age":@(2)}];
      
        // person 的dogs 属性是一个RLMArray, 在使用时不不会要创建realm 内部会自动创建
        // 只需要将 属性 添加进去即可
        [pson.dogs addObject:dog1];
        [pson.dogs addObject:dog2];
        // 3.
        [realm transactionWithBlock:^{
            //4. 添加数据模型
            [realm addObject:pson];
        }];
        NSLog(@"pson: %@",pson);
      }
    
打印结果
  • 3 、反向关系:
    比如: Person 拥有Dog,Dog 又有相应的主人.
    //Person.h 文件
    #import <Realm/Realm.h>
    #import "Dog.h"
    @interface Person : RLMObject
    @property NSInteger num;
    @property NSString  *name;
    @property int age;
    /** realm 中的一对多的关系 */
    @property Dog *dog;
    @end
    RLM_ARRAY_TYPE(Person)
    
    // Person.m 文件
    #import "Person.h"
    @implementation Person
    +(NSString *)primaryKey{
       return @"num";
    }
    @end
    
    
    //Dog.h 文件
    #import <Realm/Realm.h>
    @interface Dog : RLMObject
    @property NSInteger num;
    @property NSString  *name;
    @property int age;
    //1. 在realm 中有个特性,凡是 readonly 的属性,在生成数据库表时,该字段会自动被忽略
    //2. 在realm 中如果需要定义反向属性,就按下面这个格式定义一个属性即可,名字自己取
    //3. 不需要指定反向对象的类型, 只需要在.h文件说明反向属性的 名称,如下:
    @property(readonly)RLMLinkingObjects *master;
    @end
    RLM_ARRAY_TYPE(Dog)
    
     // Dog.m 文件
    #import "Dog.h"
    @implementation Dog
    #pragma mark- 反向映射关系描述
    // 此方法用户描述 反向属性 的映射关系
    +(NSDictionary<NSString *, RLMPropertyDescriptor*> *)linkingObjectsProperties{
     // 描述: name: 反向映射关系的属性名
     // value, RLMPropertyDescriptor 描述器, 用于说明反向属性是哪个类, 中的哪个属性
     return @{@"master":[RLMPropertyDescriptor descriptorWithClass:NSClassFromString(@"Person") propertyName:@"dog"]};
    }
    
    #pragma mark- 主键
    +(NSString *)primaryKey{
     return @"num";
    }
    @end
    
    - (void)testExample {
       //1. 获取realm 对象 (可以理解成为数据库的句柄, 对数据库的增删查改都要用到它)
       RLMRealm *realm = [RLMRealm defaultRealm];
     
       Person *pson = [[Person alloc] initWithValue:@{@"num":@(1), @"name":@"zhangsan",@"age":@(18)}];
       Dog *dog = [[Dog alloc]initWithValue:@{@"num":@(1), @"name":@"xiaoHei",@"age":@(1)}];
       pson.dog = dog;
       // 3.
       [realm transactionWithBlock:^{
           //4. 添加数据模型
           [realm addObject:pson];
       }];
     
       NSLog(@"pson: %@",pson);
     }
    
    反向关系打印结果

四、可空属性&默认值&忽略属性

  • 1、默认情况下,属性值是可空的,如果强制要求某个属性非空,可以使用下面的方法.

    (1)、遵循协议方法,如下:
    +(NSArray *)requiredProperties{
      return @[@"name1",@"name2"];
    }
    // 如果遵守了些协议方法说明了字段不能为nil ,
    //那么给对应的属性设置为nil 时后操作数据库就会报警告.
    
Snip20180813_1.png
  • 2、默认值 (遵守协议)

    +(NSDictionary *)defaultPropertyValues{
      return @{@"name":@"value"};
    }
    
  • 3、忽略属性(某些不想存入数据库的字段)
    (1)方式1 、遵守协议

    +(NSArray *)ignoredProperties{
    @[@"name", @"age"];
    }
    

    (2) 方式2 、也可使用只读属性,即

    // 只读属性,在realm 中是被忽略的.
    // 建议使用这种方式,直观
    @property(readonly)NSString *address;
    
五、通知
  • 1、realm 实例会在每次写入事物提交后,给其他线程上的realm实例发送通知.

  • 2、 realm 通知的具体用法(方式1)

    @property (nonatomic, strong)  RLMNotificationToken * token;
    // realm 的通知必须强引用,否则收不到通知
     self.token = [realm addNotificationBlock:^(RLMNotification notification, RLMRealm *realm) {
          // 接收到通知后的执行逻辑
    }];
    // 移除通知
    [self.token stop];
    

    注意:
    (1)创建通知的token 是需要强引用的,否则通知是不生效的.
    (2)移除通知时要使用对应的token

  • 3、 realm 通知的具体用法(方式2)

六、realm 数据库操作
  • 1 realm 的数据库是采用用户机制来区分数据的,不同的用户使用不同的数据库,以达到数据分离的目的
    // 封装方法,更具用户名指定realm数据库
    -(void)setDefaultRealmForUsr:(NSString *)usrName{
      
        RLMRealmConfiguration *config = [RLMRealmConfiguration defaultConfiguration];
         // 使用默认的存储目录,但是使用用户名来替换默认的文件名
        NSURL *configFileUrl  = [config.fileURL URLByDeletingLastPathComponent];
        configFileUrl = [configFileUrl URLByAppendingPathComponent:usrName];
        configFileUrl = [configFileUrl URLByAppendingPathExtension:@"realm"];
      
        // 将这个配置应用到默认的 realm 数据当中去
        [RLMRealmConfiguration setDefaultConfiguration:config];
    }
    
    // 测试
    - (void)testExample {
        // 设置默认的数据库
        [self setDefaultRealmForUsr:@"zhangsan"];
      
        //1. 获取realm 对象 (可以理解成为数据库的句柄, 对数据库的增删查改都要用到它)
        RLMRealm *realm = [RLMRealm defaultRealm];
        Person *pson = [[Person alloc]initWithValue:@{@"name":@"张三",@"age":@(14)}];
      
        [realm transactionWithBlock:^{
          [realm addObject:pson];
       }];
    }
    
七 数据库文件删除
  • 注意:
    需要删除数据库文件以及辅助文件
    -(void)deleteRealm{
        NSFileManager *fileMgr = [NSFileManager defaultManager];
      
        RLMRealmConfiguration *config = [RLMRealmConfiguration defaultConfiguration];
      
        NSArray<NSURL *> *fileUrls = @[config.fileURL,
                                     [config.fileURL URLByAppendingPathExtension:@"lock"],
                                     [config.fileURL URLByAppendingPathExtension:@"log_a"],
                                     [config.fileURL URLByAppendingPathExtension:@"log_b"],
                                     [config.fileURL URLByAppendingPathExtension:@"note"],
                                     ];
      
        for (NSURL *url in fileUrls) {
            NSError *err = nil;
            [fileMgr removeItemAtURL:url error:&err];
            if (err) {
                NSLog(@"删除 文件:%@ 失败",url);
            }
        }
        
    }
    
八、realm 数据迁移

数据迁移适用于修改了模型的情况下

  • 1 数据结构迁移
  • 2 数据迁移
  • 3 属性重命名
  • 4 多版本增量式迁移



七、realm使用总结说明:

(1)、realm 底层不是基于sqlite 数据库实现的.
(2)、realm底层是采用C++来实现的.
(3)、凡是在realm 的写事物中指定的删 改 对象代码必须是和realm中有映射关系的.

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,905评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,140评论 2 379
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,791评论 0 335
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,483评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,476评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,516评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,905评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,560评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,778评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,557评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,635评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,338评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,925评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,898评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,142评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,818评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,347评论 2 342

推荐阅读更多精彩内容