热门标签 | 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

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

推荐阅读
  • sklearn数据集库中的常用数据集类型介绍
    本文介绍了sklearn数据集库中常用的数据集类型,包括玩具数据集和样本生成器。其中详细介绍了波士顿房价数据集,包含了波士顿506处房屋的13种不同特征以及房屋价格,适用于回归任务。 ... [详细]
  • 转载自http:blog.csdn.netzhifeiyu2008articledetails8829637打开Java的JAR文件我们经常可以看到文件中包含着一个META-INF ... [详细]
  • 这是一篇CocoaChina的一个网友整理的Xcode快捷键大全,实在是太多了,我看得眼花缭乱的,不过还是非常实用,想学习就往下看吧!可能有些新手刚用mac,不知与windows键 ... [详细]
  • Spring源码解密之默认标签的解析方式分析
    本文分析了Spring源码解密中默认标签的解析方式。通过对命名空间的判断,区分默认命名空间和自定义命名空间,并采用不同的解析方式。其中,bean标签的解析最为复杂和重要。 ... [详细]
  • 搭建Windows Server 2012 R2 IIS8.5+PHP(FastCGI)+MySQL环境的详细步骤
    本文详细介绍了搭建Windows Server 2012 R2 IIS8.5+PHP(FastCGI)+MySQL环境的步骤,包括环境说明、相关软件下载的地址以及所需的插件下载地址。 ... [详细]
  • 向QTextEdit拖放文件的方法及实现步骤
    本文介绍了在使用QTextEdit时如何实现拖放文件的功能,包括相关的方法和实现步骤。通过重写dragEnterEvent和dropEvent函数,并结合QMimeData和QUrl等类,可以轻松实现向QTextEdit拖放文件的功能。详细的代码实现和说明可以参考本文提供的示例代码。 ... [详细]
  • Linux重启网络命令实例及关机和重启示例教程
    本文介绍了Linux系统中重启网络命令的实例,以及使用不同方式关机和重启系统的示例教程。包括使用图形界面和控制台访问系统的方法,以及使用shutdown命令进行系统关机和重启的句法和用法。 ... [详细]
  • 本文讨论了在Windows 8上安装gvim中插件时出现的错误加载问题。作者将EasyMotion插件放在了正确的位置,但加载时却出现了错误。作者提供了下载链接和之前放置插件的位置,并列出了出现的错误信息。 ... [详细]
  • 近来有一个需求,是需要在androidjava基础库中插入一些log信息,完成这个工作需要的前置条件有编译好的android源码具体android源码如何编译,这 ... [详细]
  • java ssm框架_Java SSM框架的简单搭建
    1.添加依赖包,可以通过properties统一框架版本UTF-81.71.75.0.8.RELEASEjunitjunit4.11testjavax.servlet ... [详细]
  • IvebeentryingforadayortwototryandgetashadowtodrawinsidethetextofanNSTextField ... [详细]
  • 2015年iOS测试现状
    本文由伯乐在线-nathanw翻译,dopcn校稿。未经许可,禁止转载!英文出处:www.mokacoding.com。欢迎加入翻译小组。几周前,我决定将将我在mokacoding ... [详细]
  • 我的iOS开发入门自学路径
    我有一个清单,列着希望在大学里完成的事。比如,学一门乐器,和朋友去旅游,搭建自己的博客,去滑翔,看各主题的书籍。其中一项是,写自己的App并且上架。去年开始,我准备完成这一项,所以 ... [详细]
  • 利用pycharm分屏编译预览latex文档
    首先确保电脑有合适的texlive发行,然后在pycharm中安装插件PDFViewer和TeXiFyIDEA,如下图所示。pluginhomepage是下载该插件的 ... [详细]
  • 动作filter上的简单注入属性注入我要注入的动作filter就像这样开始publicclassUserAuthorisation:AuthorizeAttribute{publi ... [详细]
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社区 版权所有