美文网首页
关于Realm的使用

关于Realm的使用

作者: 一米押金 | 来源:发表于2018-12-04 14:18 被阅读0次

    其实这个是我在接手项目之后看到的,我也不知道它是什么,就百度了一下,然后考虑到后续如果有商城需求,有收藏的需求,可能就会用到数据库,根据sqlite封装的FMDB很久没用了略显生疏,plist存储又有点不机动,CoreData还不太知道用法,NSUserDefault又显得不好用,所以既然这么巧,那我就考虑用下这个

    导包的话,由于我是在他基础上做的开发,他用的realm很老很老了,以至于如图示的软件都提示要更新了


    realm database.png

    而且很重要一点,要想查看数据库的东西,一定要把程序关掉,反之,如果运行程序,也要关掉数据库,不然运行之后会显示数据库在运行(可能显示别的,总之显示一个异常),也会报错

    不更新没发现什么,一更新数据库之后就发现程序崩溃了

    Unsupported Realm file format version
    

    也就是数据库已经更新了,不支持这个版本了,想再使用就难了,所以如果用的老版本的数据库,请一定一定,一定不要用这个软件打开了,如果拖入到realm数据库的话,会提示更新的问题,它会把realm数据库数据更新到很高版本,此时就会报这个错,会提示用新版本,代码本身是没有问题的

    设计数据库的字段,我这里就放代码吧,在代码里进行注解,来了解下

    Stu.h

    // 导包
    #import <Realm/Realm.h>
    #import <UIKit/UIKit.h>
    // 导入一个imgData类
    #import "ImgData.h"
    
    //继承RLMObject
    @interface Stu : RLMObject
    //对象的属性声明,注意请不要带上nonatomic,assign,strong等这样修饰的词(官方推荐避免引起奇葩错误),readonly可以但是在realm里面有新的作用,后面讲.
    // 而且要注意顺序,[[Stu alloc] initWithValue:@[@"zhangsan",@26]];这个方法,就是按照模型顺序弄的
    @property NSString *name;
    @property int num;
    // 由于image跟sqlite数据库一样,不能直接进行存储,所以只能通过NSData来进行缓冲,但是由于记录在数据中,它还是会读出来,报错,所以采取(readonly),设置只读字段来拦截
    // 只读属性会被自动忽略
    @property (readonly)UIImage *image;
    // 没传值时为:<(null) — 0 total bytes>;这个表示为可选值,也就是,可以传空
    @property NSData *imageData;
    // 定义数组变量 这个集合有个要求,这里面存储的属性,必须是继承自RLMObject类型的属性,由于是如此,比方说要存一个student对象的话所以要这么写:
    // RLMArray<student *><student> *student;
    // 针对如果说数据库要保存一组图片的方法,先在某处定义一个如此的模型,然后再定义一个专属的数组,包装一下,然后在.m方法里一个一个添加数据即可
    @property RLMArray<ImgData *><ImgData> *imgDatas;
    // 传的时间,(假设)服务器传过来的
    // 没传值为:0;
    @property NSTimeInterval time;
    // 格式化后的时间,弱业务逻辑,自己的内容
    @property NSString *timeFormatter;
    @end
    
    // This protocol enables typed collections. i.e.:
    // RLMArray<student *><student>
    // Realm中需要声明对象 -- 针对如果其他类要用到这个变量,而且是用数组封装的变量时
    RLM_ARRAY_TYPE(Stu)
    

    Stu.m

    #import "Stu.h"
    
    @implementation Stu
    // 设置num为主键,即有值,且唯一
    +(NSString *)primaryKey{
        return @"num";
    }
    
    // 设置必须要有值的参数,没有设置会报错
    +(NSArray<NSString *> *)requiredProperties{
        return @[@"name"];
    }
    
    // 用get方法获取data
    -(UIImage *)image{
        return [UIImage imageWithData:self.imageData];
    }
    
    // Specify default values for properties -- 设置默认值,就是当值为空的时候,有个默认值
    + (NSDictionary *)defaultPropertyValues
    {
        return @{@"name":@"这是侠"};
    }
    
    // Specify properties to ignore (Realm won't persist these) -- 忽略的属性列表,就是可以不传的值
    + (NSArray *)ignoredProperties
    {
        return @[@"timeFormatter"];
    }
    
    @end
    
    

    由于在Stu.h我引入了

    @property RLMArray<ImgData *><ImgData> *imgDatas;
    

    变量,为什么呢?我的想法是,图片的话,万一需求是一个类要传多张图片的时候,肯定是用数组包裹的,所以必须有个数组,数组传的是每一个imageData,如代码

    ImgData.h&.m

    #import <Realm/Realm.h>
    
    NS_ASSUME_NONNULL_BEGIN
    
    @interface ImgData : RLMObject
    @property NSData *imgData;
    @end
    
    // 必须要声明是这个type,不然导入的一方会因为缺少代码而报错
    RLM_ARRAY_TYPE(ImgData)
    NS_ASSUME_NONNULL_END
    
    #import "ImgData.h"
    
    @implementation ImgData
    
    @end
    

    使用:增

    -(void)realmAdd{
        //1.快速Realm创建对象
        //第一种
    //    Stu *stu1 = [[Stu alloc] initWithValue:@{@"num":@2,@"name":@"xxx"}];
    //    //第二种(顺序是和声明的顺序一致,目前不推荐采用)
    //    Stu *stu2  = [[Stu alloc] initWithValue:@[@"zhangsan",@26]];
    
        //第三种(推荐这个,逻辑严密)
        Stu *stu3 = [[Stu alloc] init];
        stu3.name = @"网二";
        stu3.num = 102;
    
        //2.存储对象(realm种存储对象一定要开启事务)
        RLMRealm *realm = [RLMRealm defaultRealm];
    
        //第一种方法
        //开启事务
        [realm beginWriteTransaction];
        //存储对象
        [realm addObject:stu3];
        //提交事务
        [realm commitWriteTransaction];
    
        //第二种方法(推荐这个,逻辑比较快)
        [realm transactionWithBlock:^{
            [realm addObject:stu3];
        }];
    
        //第三种 地址在po NSHomeDirectory(),在documents里
    //    [realm transactionWithBlock:^{
    //        [Stu createInRealm:realm withValue:@[@"zhangsan",@28]];
    //    }];
    }
    

    我这里把方法都柔和在一起了,希望能看得懂,我也选择了我喜欢的策略,附有“推荐二字”

    RLMRealm *realm = [RLMRealm defaultRealm];
        Stu *stu = [[Stu alloc] init];
        stu.name = @"王瑞文";
        stu.num = 104;
    //    stu.image = [UIImage imageNamed:@"video_supervision_video_bg"];
        NSString *filePath = [[NSBundle mainBundle]pathForResource:@"video_supervision_video_bg.png" ofType:nil];
        NSData *data = [NSData dataWithContentsOfFile:filePath];
        stu.imageData = data;
    //  如果看到以下错误:
        //Terminating app due to uncaught exception 'RLMException', reason: 'Migration is required due to the following errors:
        //- Property 'Stu.name' has been made required.'
        // 即表示需要进行数据迁移操作,在一个类里添加或者修改了一个参数,或者添加了一个方法声明,都会发生这个操作
        // 由于表结构发生了改变,则需要发生数据迁移
    
        // 设置data数组模型
        ImgData *data1 = [[ImgData alloc]init];
        data1.imgData =  [NSData dataWithContentsOfFile:filePath];
        ImgData *data2 = [[ImgData alloc]init];
        data2.imgData = [NSData dataWithContentsOfFile:filePath];
        // RLM数组可以使用NSMutableArray添加数组的方法
        [stu.imgDatas addObject:data1];
        [stu.imgDatas addObject:data2];
    
    
        Stu *stu2 = [[Stu alloc] init];
        stu2.name = @"王文瑞";
        stu2.num = 102;
    
        Stu *stu3 = [[Stu alloc] init];
        stu3.name = @"网二小";
        stu3.num = 101;
    
        Stu *stu4 = [[Stu alloc] init];
        stu4.name = @"王文瑞";
        stu4.num = 103;
    
        Stu *stu5 = [[Stu alloc] init];
        stu5.num = 108;
        //  添加或修改,就是如果没有与之主键冲突的就添加,有就修改,在自己不确定自己是否添加的时候,可以使用addOrUpdateObject方法
        [realm transactionWithBlock:^{
            [realm addOrUpdateObject:stu];
            [realm addOrUpdateObject:stu2];
            [realm addOrUpdateObject:stu3];
            [realm addOrUpdateObject:stu4];
            [realm addOrUpdateObject:stu5];
        }];
    

    这块位置,我不但推荐拿来修改,也可以拿来添加,保证了很高的容错率

    -(void)deleteOperation{
        RLMRealm *realm = [RLMRealm defaultRealm];
    
        // 查询所有结果
        //RLMResults *results = [Stu allObjects];
        // 根据主键,查到这个模型(这个模型,必须是被realm数据库管理的模型),所以主键要写对
        Stu *mainKeyRes = [Stu objectInRealm:realm forPrimaryKey:@101];
    
        // 通过名字查询结果
        RLMResults *results = [Stu objectsWhere:@"name = '王文瑞'"];
        NSLog(@"%@",results);
    
        // 获取结果集第一个然后删除
        Stu *stu = results.firstObject;
        //  删除的模型,一定要求是被realm所管理的
        [realm transactionWithBlock:^{
            // 删除所有数据
            //[realm deleteAllObjects];
            // 删除指定数据,但是这个指定数据,一定是要在数据库里的,不然会崩
            [realm deleteObject:mainKeyRes];
            RLMResults *Allresults = [Stu allObjects];
            NSLog(@"%@",Allresults);
        }];
    }
    

    查 (这里只列举了一般的,如果还有更多需求的,可以结合删除需求一起来看一看)

    -(void)searchOpertaion{
        RLMResults *Allresults = [Stu allObjects];
        // 排序查询 ascending:NO由高到低 YES:由低到高
        RLMResults *sortResults = [Allresults sortedResultsUsingProperty:@"num" ascending:YES];
        NSLog(@"%@",sortResults);
    }
    

    查这个问题上,视频老师也提出了很多需求,比方说多线程,懒加载之类的,说实话我没有听太懂,可能是指,很消耗性能?所以到底是怎么使用呢?也可能是提示使用者查找数据要精细,精确到哪个数据点,还是说其他的用途

    其他遇到的

    例如导包的时候发觉 :导入realm.framework的时候出现image not found的处理https://blog.csdn.net/knaht/article/details/80505648
    这个链接我调试过,也成功运行了,如果版本很高没遇到这个问题,那可以选择性忽略

    数据库配置

    我的意思就是可以生成不同数据库文件名的一种策略

    // 用做切换用户的,就是生成不同文件名,产生不同的数据库
    // 然后剩下注意的,就是生成不同的realm对象就可以了
    // 比方说我有两个类,但是两个类不想存在一个数据库里,怎么区分呢?
    -(void)setDefaultRealmForUser:(NSString *)username{
        RLMRealmConfiguration *config = [RLMRealmConfiguration defaultConfiguration];
        // 使用默认的目录,但是使用用户名来替换默认的文件名
        config.fileURL = [[[config.fileURL URLByDeletingLastPathComponent] URLByAppendingPathComponent:username] URLByAppendingPathExtension:@"realm"];
        // 将这个配置应用到默认的Realm 数据库当中
        [RLMRealmConfiguration setDefaultConfiguration:config];
    
    }
    

    数据迁移

    这个就是用在比方说,Stu类,我在以前的基础上添加了一个字段,然后存储,这样子,肯定跟之前的发生了冲突,Realm就会报错,提示
    //Terminating app due to uncaught exception 'RLMException', reason: 'Migration is required due to the following errors:
    //- Property 'Stu.name' has been made required.
    需要迁移版本,所以就有如此做法

    /**
     数据迁移
     */
    -(void)dataLeaper
    {
        // 先做数据迁移操作 -- 一般设置在'appdelegate didfinishLaunchingxxxxxxx'方法里面设置效率最好
        // 获取默认配置
        RLMRealmConfiguration *config = [RLMRealmConfiguration defaultConfiguration];
        // 叠加版本号(要比上一次的版本号高)例如上一次是0这次就要比上次要高
        int newVersion = 1;
        config.schemaVersion = newVersion;
        // 具体怎样迁移?
        [config setMigrationBlock:^(RLMMigration * _Nonnull migration, uint64_t oldSchemaVersion) {
            if (oldSchemaVersion < newVersion) {//判定,只有小于新版本的时候才有迁移操作
                // 不需要做任何事情,就可以完成数据结构,已经数据的迁移
                NSLog(@"需要做迁移动作");
            }
        }];
        // 让配置生效
        [RLMRealmConfiguration setDefaultConfiguration:config];
        // 当然这些都是在配置,如果需要立即迁移,就要访问一下realm,而且迁移之后,之前的数据都在存在
        [RLMRealm defaultRealm];
    }
    
    可能还有需求,比方说,我新建了两个字段"firstName","lastName",但是新的需求是,添加了一个"fullName",它="firName"+"lastName",这个冲突就打了,加新的,去老的,而且要传值,这个做法我就上图了 otherLeapNeed.png

    这里的Migration就是一个类名,表示,在哪个类做这样子的数据配置啊?
    newObject:新的对象,新的对象里的什么字段=旧的对象里的什么字段啊,这样子遍历下来,得出的,所以看得出,是一个耗时方法,它需要遍历数据库里每一个元素,进行更改操作

    还有个需求,"fullName"字段改了,改成了"fuName",这个也是有可能的,版本又要迁移了,怎么办?如图:


    shuxingbiangeng.png

    相关文章

      网友评论

          本文标题:关于Realm的使用

          本文链接:https://www.haomeiwen.com/subject/fapmcqtx.html