热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

iOS开发之realm本地存储

git地址:https://github.com/realm/realm-cocoa官网地址:https://realm.io/xcode工具包:https://

git地址:https://github.com/realm/realm-cocoa
官网地址:https://realm.io/
xcode工具包:https://github.com/realm/realm-cocoa
realm studio下载地址:https://github.com/realm/realm-studio

1.准备阶段

配置开发环境:pod比较简单
如果单元测试中要使用要记得在单元测试target中同样引入库

# Uncomment the next line to define a global platform for your project # platform :ios, '9.0' target 'TestRealm' do # Comment the next line if you don't want to use dynamic frameworks use_frameworks! pod 'Realm' # Pods for TestRealm target 'TestRealmTests' do inherit! :search_paths pod 'Realm' # Pods for testing end target 'TestRealmUITests' do # Pods for testing end end

安装数据库管理工具:Real studio(https://github.com/realm/realm-studio)

iOS开发之realm本地存储
![截屏2020-09-22 下午8.24.35.png](https://upload-images.jianshu.io/upload_images/3692367-fca95f008ef452cb.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

安装xcode插件包:(官网就有)

iOS开发之realm本地存储
2.基本使用

2.1 引入文件头

#import

2.2 利用插件创建模型类

iOS开发之realm本地存储

模型类中引入属性要注意写法

#import @interface Student : RLMObject @property int student_number; @property NSString *name; @end // This protocol enables typed collections. i.e.: // RLMArray RLM_ARRAY_TYPE(Student)

.m文件中设置通过复写primaryKey方法设置主键

+ (NSString *)primaryKey{ return "student_number"; }

2.3 保存写入数据

// 可以用数组,也可以用字典,(如果用数组要保证和模型一致) // Student *student = [[Student alloc]initWithValue:@{@"student_number":@1,@"name":@"cy"}]; Student *student = [[Student alloc]initWithValue:@[@1,@"cy"]]; RLMRealm *realm = [RLMRealm defaultRealm]; //普通写法 // [realm beginWriteTransaction];// 开始写入 // [realm addObject:student];// 存储数据 //[realm comm itWriteTransaction];//提交 // 也可以这样写 [realm transactionWithBlock:^{ [realm addObject:student]; }];

也可以用不用初始化数据模型用以下方法实现

RLMRealm *realm = [RLMRealm defaultRealm]; [realm transactionWithBlock:^{ [Student createInDefaultRealmWithValue:@[@2,@"liutiesong"]]; }];

通过realm studio打开文件查看写入数据

iOS开发之realm本地存储

2.4 更新数据

注:

  • 更新数据前要先写入数据
  • 更新的数据模型对象必须是被realm所持有的对象[图片上传中…(截屏2020-09-27 下午12.38.23.png-45f614-1601182593843-0)]

Student *student = [[Student alloc]initWithValue:@[@3,@"test"]]; RLMRealm *realm = [RLMRealm defaultRealm]; [realm transactionWithBlock:^{ [realm addObject:student]; }]; [realm transactionWithBlock:^{ student.name = @"finalTest"; }];

iOS开发之realm本地存储
addObject时输出

iOS开发之realm本地存储
最后更新输出

2.5 查找数据并更新

RLMRealm *realm = [RLMRealm defaultRealm]; RLMResults *result = [Student objectsWhere:@"name = 'finalTest'"]; Student *student = result.firstObject; [realm transactionWithBlock:^{ student.name = @"cy"; }];

2.6 添加/创建 并 更新/覆盖 数据

注意:

  • 必须设置主键否则会报错(添加方法看起始步骤)
  • 添加数据时如果主键相同,则会进行修改
添加更新数据

Student *student = [[Student alloc]initWithValue:@[@2,@"test-2"]]; RLMRealm *realm = [RLMRealm defaultRealm]; [realm transactionWithBlock:^{ [realm addOrUpdateObject:student]; }];

创建更新数据

RLMRealm *realm = [RLMRealm defaultRealm]; [realm transactionWithBlock:^{ [Student createOrUpdateInRealm:realm withValue:@[@2,@"test2"]]; }];

2.8 删除数据

2.8.1 查找出指定数据并删除

RLMRealm *realm = [RLMRealm defaultRealm]; RLMResults *result = [Student objectsWhere:@"name = 'test2'"]; Student *student = result.firstObject; [realm transactionWithBlock:^{ [realm deleteObject:student]; }];

2.8.2 删除库内所有数据

RLMRealm *realm = [RLMRealm defaultRealm]; [realm transactionWithBlock:^{ [realm deleteAllObjects]; }];

2.8.3 删除制定模型库内所有数据

RLMRealm *realm = [RLMRealm defaultRealm]; RLMResults *results = [Student allObjects]; for (Student *student in results) { [realm transactionWithBlock:^{ [realm deleteObject:student]; }]; }

2.8.4 根据主键删除制定模型

Student *student = [Student objectInRealm:realm forPrimaryKey:@2]; [realm transactionWithBlock:^{ [realm deleteObject:student]; }];

2.9 查询

2.9.1 查询所有数据

RLMResults *studentResult = [Student allObjects]; NSLog(@"%@",studentResult);

2.9.2 条件查询

RLMResults *result = [Student objectsWhere:@"name = 'test2'"];

2.9.3 参照属性排序(将查询出来的结果根据参数进行排序)

RLMResults *studentResult = [Student allObjects]; RLMResults *sortedResult = [studentResult sortedResultsUsingKeyPath:@"name" ascending:YES];

2.9.4 链式查询

可以支持多条件链式叠加查询

RLMResults *studentResult = [Student objectsWhere:@"student_number > 2"]; RLMResults *secOndStudentResults= [studentResult objectsWhere:@"student_number

2.9.5 分页查询

RLMResults *allStudents = [Student allObjects]; int limit = 2; int offset = 1; for (int i = offset; i

3.其他使用注意

3.1 使用模型存储 图片数据

在属性声明时,realm不支持的类型可以在.m文件中复写ignore方法,或者使用readonly声明

@property (readonly)UIImage *image; @property NSData *imageData;

+ (NSArray *)ignoredProperties { return @[@"image"]; } - (UIImage *)image{ return [UIImage imageWithData:self.imageData];; }

↑当需要读取UIImage时,可以重写image的set方法来获取图片数据(在数据模型的.m文件中)

数据存储代码:

// 可以用数组,也可以用字典,(如果用数组要保证和模型一致) RLMRealm *realm = [RLMRealm defaultRealm]; Student *student = [[Student alloc]init]; student.name = @"cy"; student.student_number = 1; NSString *filePath = [[NSBundle mainBundle]pathForResource:@"1.jpg" ofType:nil]; student.imageData = [NSData dataWithContentsOfFile:filePath]; [realm transactionWithBlock:^{ [realm addOrUpdateObject:student]; }];

4. Realm中的表对应关系

  • realm中不可直接使用NSArray,如要使用列表需要使用RLMArray类型
  • RLMArray类型的数据里面存储的对象必须是继承自RLMObject类型的属性
    student.h类中:

#import "StudentDetails.h" @property RLMArray *details;

创建一个RLMObject类型的模型用来存放数组内数据:

#import @interface StudentDetails : RLMObject @property NSString *detailID; @end // This protocol enables typed collections. i.e.: // RLMArray RLM_ARRAY_TYPE(StudentDetails)

4.1 一对一关系

创建两个数据模型Zoo和Cow,Zoo中包含一个Cow对象如下:

  • Zoo类

#import #import "Cow.h" @interface Zoo : RLMObject @property int zooId; @property NSString *name; @property Cow *cow; @end RLM_ARRAY_TYPE(Zoo)

  • Cow类

#import @interface Cow : RLMObject @property int cowId; @property NSString *name; @end RLM_ARRAY_TYPE(Cow)

设置对应的 cowId和zooId为主键

  • 添加数据:

Zoo *zoo = [Zoo new]; zoo.zooId = 1; zoo.name = @"木有鱼丸"; Cow *cow = [Cow new]; cow.cowId = 1; cow.name = @"奶牛1号"; zoo.cow = cow; RLMRealm *realm = [RLMRealm defaultRealm]; [realm transactionWithBlock:^{ [realm addObject:zoo]; }];

iOS开发之realm本地存储

iOS开发之realm本地存储

4.2 一对多关系

  • zoo类

#import #import "Cow.h" @interface Zoo : RLMObject @property int zooId; @property NSString *name; @property RLMArray *cows; @end RLM_ARRAY_TYPE(Zoo)

  • cow类不变同上
  • 添加数据

Zoo *zoo = [Zoo new]; zoo.zooId = 1; zoo.name = @"木有鱼丸"; Cow *cow = [Cow new]; cow.cowId = 1; cow.name = @"奶牛1号"; Cow *cow2 = [Cow new]; cow2.cowId = 2; cow2.name = @"奶牛2号"; [zoo.cows addObject:cow]; [zoo.cows addObject:cow2]; RLMRealm *realm = [RLMRealm defaultRealm]; [realm transactionWithBlock:^{ [realm addObject:zoo]; }];

  • 数据库:

    iOS开发之realm本地存储

    iOS开发之realm本地存储

4.3 反向关系

  • zoo类同上不变
  • cow类如下

1.使用@class声明避免循环引用,使用RLMLinkingObjects创建zoo属性

#import @class Zoo; @interface Cow : RLMObject @property int cowId; @property NSString *name; @property(readonly) RLMLinkingObjects *zoo; @end RLM_ARRAY_TYPE(Cow)

2.m文件中通过协议方法构建反向关系

#import "Cow.h" @implementation Cow + (NSString *)primaryKey{ return @"cowId"; } // 关联方法 + (NSDictionary *)linkingObjectsProperties{ // 通过属性描述器关联 return @{@"zoo":[RLMPropertyDescriptor descriptorWithClass:NSClassFromString(@"Zoo") propertyName:@"cows"]}; } @end

测试获取数据

Zoo *zoo = [Zoo allObjects].firstObject; NSLog(@"打印数据%@",zoo.cows.firstObject.zoo);

2020-09-29 18:47:53.510317+0800 TestRealm[30907:35745028] 打印数据RLMLinkingObjects ( [0] Zoo { zooId = 1; name = 木有鱼丸; cows = RLMArray ( [0] Cow { cowId = 1; name = 奶牛1号; }, [1] Cow { cowId = 2; name = 奶牛2号; } ); } )

5. Realm中的属性

5.1强制非空

默认情况下,属性是可空的,如果想要强制要求某个属性必须非空可以如下操作:

+ (NSArray *)requiredProperties{ return @[@"name"];; }

5.2 给属性自定义默认值

+ (NSDictionary *)defaultPropertyValues{ return @{@"name":@"数据不存在"}; }

5.3 忽略存储属性,过滤掉不需要存储的属性

  • 方法一:(直接使用readonly声明属性)

@property(readonly) NSString *name;

  • 方法二:.m中复写协议方法

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

6.realm中的消息通知

  • 监听添加数据:

@property(nonatomic,strong) RLMNotificationToken *token; - (void)setUp{ [super setUp]; RLMRealm *realm = [RLMRealm defaultRealm]; self.token = [realm addNotificationBlock:^(RLMNotification _Nonnull notification, RLMRealm * _Nonnull realm) { NSLog(@"监听到通知"); }]; } - (void)tearDown{ [self.token invalidate]; [super tearDown]; } - (void)testOneToOne{ Cow *cow = [[Cow alloc]initWithValue:@{@"cowId":@1,@"name":@"test"}]; RLMRealm *realm = [RLMRealm defaultRealm]; [realm transactionWithBlock:^{ [realm addObject:cow]; }]; }

  • 监听result查询数据

@property(nonatomic,strong) RLMNotificationToken *token; - (void)setUp{ [super setUp]; RLMResults *result = [Cow allObjects]; self.token = [result addNotificationBlock:^(RLMResults * _Nullable results, RLMCollectionChange * _Nullable change, NSError * _Nullable error) { NSLog(@"%@____%@___%@",results,change,error); }]; } - (void)tearDown{ [self.token invalidate];//注销监听 [super tearDown]; } - (void)testOneToOne{ Cow *cow = [[Cow alloc]initWithValue:@{@"cowId":@1,@"name":@"test"}]; RLMRealm *realm = [RLMRealm defaultRealm]; [realm transactionWithBlock:^{ [realm addObject:cow]; }]; }

可以在RLMCollectionChange中获取具体修改的内容

7. realm中的多用户数据库区分机制

创建数据模型

#import @interface UserData : RLMObject @property int userDataId; @end RLM_ARRAY_TYPE(UserData)

通过修改尾缀根据用户区分数据库

- (void)testExample { [self setDefalutRealmForUser:@"test1"]; RLMRealm *realm = [RLMRealm defaultRealm]; UserData *data = [UserData new]; data.userDataId = 1; [realm transactionWithBlock:^{ [realm addObject:data]; }]; [self setDefalutRealmForUser:@"test2"]; RLMRealm *realm2 = [RLMRealm defaultRealm]; UserData *data2 = [UserData new]; data2.userDataId = 1; [realm2 transactionWithBlock:^{ [realm2 addObject:data2]; }]; } - (void)setDefalutRealmForUser:(NSString*)userId{ RLMRealmConfiguration *cOnfig= [RLMRealmConfiguration defaultConfiguration]; config.fileURL = [[[config.fileURL URLByDeletingLastPathComponent]URLByAppendingPathComponent:userId] URLByAppendingPathExtension:@"realm"]; [RLMRealmConfiguration setDefaultConfiguration:config]; }

8.以只读方式打开数据库

将config中的readObly参数设置为YES

- (void)testReadOnly{ RLMRealmConfiguration *cOnfig= [RLMRealmConfiguration defaultConfiguration]; config.readOnly= YES; RLMRealm *realm = [RLMRealm realmWithConfiguration:config error:nil]; UserData *data = [UserData new]; data.userDataId = 100; [realm transactionWithBlock:^{ [realm addObject:data]; }]; }

9. 数据库文件删除

  • 删除test1数据库文件

- (void)testDeleteDataBase{ [self setDefalutRealmForUser:@"test1"]; NSFileManager *manager = [NSFileManager defaultManager]; RLMRealmConfiguration *cOnfig= [RLMRealmConfiguration defaultConfiguration]; NSArray *realmFileURLS = @[ config.fileURL, [config.fileURL URLByAppendingPathExtension:@"lock"], [config.fileURL URLByAppendingPathExtension:@"log_a"], [config.fileURL URLByAppendingPathExtension:@"log_b"], [config.fileURL URLByAppendingPathExtension:@"note"] ]; for (NSURL *url in realmFileURLS) { NSError *error = nil; [manager removeItemAtURL:url error:&error]; if (error) { //容错处理 } } }

10.数据库迁移

10.1 数据库结构迁移
  • 数据模型修改(添加userName属性)

#import @interface UserData : RLMObject @property int userDataId; @end RLM_ARRAY_TYPE(UserData)

#import @interface UserData : RLMObject @property int userDataId; @property NSString *userName; @end RLM_ARRAY_TYPE(UserData)

  • 数据库迁移配置(一般放在appdelegate中,每次修改要更新数据库版本号)

- (void)setUp{ [super setUp]; RLMRealmConfiguration *cOnfig= [RLMRealmConfiguration defaultConfiguration]; // 叠加版本号(每次更新版本号要比上一次高) int newVersion = 1; config.schemaVersion = newVersion; // 数据迁移 [config setMigrationBlock:^(RLMMigration * _Nonnull migration, uint64_t oldSchemaVersion) { if (oldSchemaVersion

  • 数据迁移操作

- (void)testMigration{ UserData *user = [UserData new]; user.userDataId = 1; user.userName = @"cy"; RLMRealm *realm = [RLMRealm defaultRealm]; [realm transactionWithBlock:^{ [realm addObject:user]; }]; }

iOS开发之realm本地存储
数据库迁移前

iOS开发之realm本地存储
数据库迁移后
  • 合并或保留旧数据:
    原数据库数据:

    iOS开发之realm本地存储

    迁移后数据:

    iOS开发之realm本地存储

    ** 代码 **
    模型层修改

#import @interface UserData : RLMObject @property int userDataId; //@property NSString *firstName; //@property NSString *lastName; @property NSString *name; @end RLM_ARRAY_TYPE(UserData)

数据库迁移操作

- (void)setUp{ [super setUp]; RLMRealmConfiguration *cOnfig= [RLMRealmConfiguration defaultConfiguration]; // 叠加版本号(每次更新版本号要比上一次高) int newVersion = 5; config.schemaVersion = newVersion; // 数据迁移 [config setMigrationBlock:^(RLMMigration * _Nonnull migration, uint64_t oldSchemaVersion) { if (oldSchemaVersion

  • 高效率修改属性名称(name -> fullname)

#import @interface UserData : RLMObject @property int userDataId; //@property NSString *firstName; //@property NSString *lastName; //@property NSString *name; @property NSString *fullName; @end RLM_ARRAY_TYPE(UserData)

- (void)setUp{ [super setUp]; RLMRealmConfiguration *cOnfig= [RLMRealmConfiguration defaultConfiguration]; // 叠加版本号(每次更新版本号要比上一次高) int newVersion = 4; config.schemaVersion = newVersion; // 数据迁移 [config setMigrationBlock:^(RLMMigration * _Nonnull migration, uint64_t oldSchemaVersion) { if (oldSchemaVersion

  • 其他:
    注意版本控制与兼容,可以通过版本判断来区分各个版本修改的数据库代码

推荐阅读
  • 在软件开发过程中,经常需要将多个项目或模块进行集成和调试,尤其是当项目依赖于第三方开源库(如Cordova、CocoaPods)时。本文介绍了如何在Xcode中高效地进行多项目联合调试,分享了一些实用的技巧和最佳实践,帮助开发者解决常见的调试难题,提高开发效率。 ... [详细]
  • 在对WordPress Duplicator插件0.4.4版本的安全评估中,发现其存在跨站脚本(XSS)攻击漏洞。此漏洞可能被利用进行恶意操作,建议用户及时更新至最新版本以确保系统安全。测试方法仅限于安全研究和教学目的,使用时需自行承担风险。漏洞编号:HTB23162。 ... [详细]
  • JUC(三):深入解析AQS
    本文详细介绍了Java并发工具包中的核心类AQS(AbstractQueuedSynchronizer),包括其基本概念、数据结构、源码分析及核心方法的实现。 ... [详细]
  • 本文介绍了几种常用的图像相似度对比方法,包括直方图方法、图像模板匹配、PSNR峰值信噪比、SSIM结构相似性和感知哈希算法。每种方法都有其优缺点,适用于不同的应用场景。 ... [详细]
  • 本文介绍了如何利用HTTP隧道技术在受限网络环境中绕过IDS和防火墙等安全设备,实现RDP端口的暴力破解攻击。文章详细描述了部署过程、攻击实施及流量分析,旨在提升网络安全意识。 ... [详细]
  • 秒建一个后台管理系统?用这5个开源免费的Java项目就够了
    秒建一个后台管理系统?用这5个开源免费的Java项目就够了 ... [详细]
  • 本文详细介绍了 InfluxDB、collectd 和 Grafana 的安装与配置流程。首先,按照启动顺序依次安装并配置 InfluxDB、collectd 和 Grafana。InfluxDB 作为时序数据库,用于存储时间序列数据;collectd 负责数据的采集与传输;Grafana 则用于数据的可视化展示。文中提供了 collectd 的官方文档链接,便于用户参考和进一步了解其配置选项。通过本指南,读者可以轻松搭建一个高效的数据监控系统。 ... [详细]
  • 解决针织难题:R语言编程技巧与常见错误分析 ... [详细]
  • NOIP2000的单词接龙问题与常见的成语接龙游戏有异曲同工之妙。题目要求在给定的一组单词中,从指定的起始字母开始,构建最长的“单词链”。每个单词在链中最多可出现两次。本文将详细解析该题目的解法,并分享学习过程中的心得体会。 ... [详细]
  • 技术日志:使用 Ruby 爬虫抓取拉勾网职位数据并生成词云分析报告
    技术日志:使用 Ruby 爬虫抓取拉勾网职位数据并生成词云分析报告 ... [详细]
  • 本文深入探讨了 iOS 开发中 `int`、`NSInteger`、`NSUInteger` 和 `NSNumber` 的应用与区别。首先,我们将详细介绍 `NSNumber` 类型,该类用于封装基本数据类型,如整数、浮点数等,使其能够在 Objective-C 的集合类中使用。通过分析这些类型的特性和应用场景,帮助开发者更好地理解和选择合适的数据类型,提高代码的健壮性和可维护性。苹果官方文档提供了更多详细信息,可供进一步参考。 ... [详细]
  • 本文介绍如何通过 Python 的 `unittest` 和 `functools` 模块封装一个依赖方法,用于管理测试用例之间的依赖关系。该方法能够确保在某个测试用例失败时,依赖于它的其他测试用例将被跳过。 ... [详细]
  • 结城浩(1963年7月出生),日本资深程序员和技术作家,居住在东京武藏野市。他开发了著名的YukiWiki软件,并在杂志上发表了大量程序入门文章和技术翻译作品。结城浩著有30多本关于编程和数学的书籍,其中许多被翻译成英文和韩文。 ... [详细]
  • 解决Bootstrap DataTable Ajax请求重复问题
    在最近的一个项目中,我们使用了JQuery DataTable进行数据展示,虽然使用起来非常方便,但在测试过程中发现了一个问题:当查询条件改变时,有时查询结果的数据不正确。通过FireBug调试发现,点击搜索按钮时,会发送两次Ajax请求,一次是原条件的请求,一次是新条件的请求。 ... [详细]
  • 第六章:枚举类型与switch结构的应用分析
    第六章深入探讨了枚举类型与 `switch` 结构在编程中的应用。枚举类型(`enum`)是一种将一组相关常量组织在一起的数据类型,广泛存在于多种编程语言中。例如,在 Cocoa 框架中,处理文本对齐时常用 `NSTextAlignment` 枚举来表示不同的对齐方式。通过结合 `switch` 结构,可以更清晰、高效地实现基于枚举值的逻辑分支,提高代码的可读性和维护性。 ... [详细]
author-avatar
Wo-们是平行线
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有