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)
![截屏2020-09-22 下午8.24.35.png](https://img.haomeiwen.com/i3692367/fca95f008ef452cb.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)安装xcode插件包:(官网就有)
2.基本使用
2.1 引入文件头
#import <Realm/Realm.h>
2.2 利用插件创建模型类
模型类中引入属性要注意写法
#import <Realm/Realm.h>
@interface Student : RLMObject
@property int student_number;
@property NSString *name;
@end
// This protocol enables typed collections. i.e.:
// RLMArray<Student *><Student>
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打开文件查看写入数据
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";
}];
addObject时输出
最后更新输出
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<Student*> *studentResult = [Student allObjects];
RLMResults *sortedResult = [studentResult sortedResultsUsingKeyPath:@"name" ascending:YES];
2.9.4 链式查询
可以支持多条件链式叠加查询
RLMResults<Student*> *studentResult = [Student objectsWhere:@"student_number > 2"];
RLMResults *secondStudentResults = [studentResult objectsWhere:@"student_number < 4"];
2.9.5 分页查询
RLMResults *allStudents = [Student allObjects];
int limit = 2;
int offset = 1;
for (int i = offset; i<offset + limit; i++) {
Student *student = allStudents[i];
NSLog(@"%@",student);
}
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 <StudentDetails*><StudentDetails>*details;
创建一个RLMObject类型的模型用来存放数组内数据:
#import <Realm/Realm.h>
@interface StudentDetails : RLMObject
@property NSString *detailID;
@end
// This protocol enables typed collections. i.e.:
// RLMArray<StudentDetails *><StudentDetails>
RLM_ARRAY_TYPE(StudentDetails)
4.1 一对一关系
创建两个数据模型Zoo和Cow,Zoo中包含一个Cow对象如下:
- Zoo类
#import <Realm/Realm.h>
#import "Cow.h"
@interface Zoo : RLMObject
@property int zooId;
@property NSString *name;
@property Cow *cow;
@end
RLM_ARRAY_TYPE(Zoo)
- Cow类
#import <Realm/Realm.h>
@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];
}];
4.2 一对多关系
- zoo类
#import <Realm/Realm.h>
#import "Cow.h"
@interface Zoo : RLMObject
@property int zooId;
@property NSString *name;
@property RLMArray<Cow *><Cow> *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];
}];
-
数据库:
4.3 反向关系
- zoo类同上不变
- cow类如下
1.使用@class声明避免循环引用,使用RLMLinkingObjects创建zoo属性
#import <Realm/Realm.h>
@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<NSString *,RLMPropertyDescriptor *> *)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<Zoo> <0x7fbf92c1c740> (
[0] Zoo {
zooId = 1;
name = 木有鱼丸;
cows = RLMArray<Cow> <0x60000322f340> (
[0] Cow {
cowId = 1;
name = 奶牛1号;
},
[1] Cow {
cowId = 2;
name = 奶牛2号;
}
);
}
)
5. Realm中的属性
5.1强制非空
默认情况下,属性是可空的,如果想要强制要求某个属性必须非空可以如下操作:
+ (NSArray<NSString *> *)requiredProperties{
return @[@"name"];;
}
5.2 给属性自定义默认值
+ (NSDictionary *)defaultPropertyValues{
return @{@"name":@"数据不存在"};
}
5.3 忽略存储属性,过滤掉不需要存储的属性
- 方法一:(直接使用readonly声明属性)
@property(readonly) NSString *name;
- 方法二:.m中复写协议方法
+ (NSArray<NSString *> *)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 <Realm/Realm.h>
@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<NSURL*> *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 <Realm/Realm.h>
@interface UserData : RLMObject
@property int userDataId;
@end
RLM_ARRAY_TYPE(UserData)
#import <Realm/Realm.h>
@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 < newVersion) {
// 不需要进行操作,自动完成数据结构、数据迁移
NSLog(@"需要迁移数据库");
}
}];
// 配置生效
[RLMRealmConfiguration setDefaultConfiguration:config];
// 立即迁移
[RLMRealm defaultRealm];
}
- 数据迁移操作
- (void)testMigration{
UserData *user = [UserData new];
user.userDataId = 1;
user.userName = @"cy";
RLMRealm *realm = [RLMRealm defaultRealm];
[realm transactionWithBlock:^{
[realm addObject:user];
}];
}
数据库迁移前
数据库迁移后
-
合并或保留旧数据:
原数据库数据:
迁移后数据:
** 代码 **
模型层修改
#import <Realm/Realm.h>
@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 < newVersion) {
// 不需要进行操作,自动完成数据结构、数据迁移
NSLog(@"需要迁移数据库");
[migration enumerateObjects:@"UserData" block:^(RLMObject * _Nullable oldObject, RLMObject * _Nullable newObject) {
newObject[@"name"] = [NSString stringWithFormat:@"%@ %@",oldObject[@"lastName"],oldObject[@"firstName"]];
}];
}
}];
// 配置生效
[RLMRealmConfiguration setDefaultConfiguration:config];
// 立即迁移
[RLMRealm defaultRealm];
}
- (void)testMigration{
UserData *user = [UserData new];
user.userDataId = 4;
user.name = @"liu new";
RLMRealm *realm = [RLMRealm defaultRealm];
[realm transactionWithBlock:^{
[realm addObject:user];
}];
}
- 高效率修改属性名称(name -> fullname)
#import <Realm/Realm.h>
@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 < newVersion) {
// 不需要进行操作,自动完成数据结构、数据迁移
NSLog(@"需要迁移数据库");
[migration renamePropertyForClass:@"UserData" oldName:@"name" newName:@"fullName"];
// [migration enumerateObjects:@"UserData" block:^(RLMObject * _Nullable oldObject, RLMObject * _Nullable newObject) {
// newObject[@"name"] = [NSString stringWithFormat:@"%@ %@",oldObject[@"lastName"],oldObject[@"firstName"]];
// }];
}
}];
// 配置生效
[RLMRealmConfiguration setDefaultConfiguration:config];
// 立即迁移
[RLMRealm defaultRealm];
}
- 其他:
注意版本控制与兼容,可以通过版本判断来区分各个版本修改的数据库代码
网友评论