关于Realm的使用

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

推荐阅读更多精彩内容