realm是跨平台开发,使用C++编写,并是不对sqlite的封装,是有独立的数据库存储引擎.比sqlite和coredate性能还好.
官网介绍的realm支持的数据类型
说明:为方便测试,这里我使用Xcode自带的单元测试来操作.
准备工作:
1.去 官网下载realm,我这里选择的是realm-Objc
2.下载realm-objc安装包,打开realm-objc-2.9.1-->plugin-->RealmPlugin,安装realm的插件.直接编译运行即可,安装完成后即有个realm的模型
realm简单使用步骤
1.新建一个工程,通过cocoapods方式继承到项目中.
首次安装时间可能久点,看到以下信息代表安装成功
Sending stats
- Realm, 3.0.0-beta.2
-> Pod installation complete! There is 1 dependency from the Podfile and 1 total pod installed.
2创建单元测试模块
这里我们直接在RealmDemoTests文件中导入 #import
Realm/Realm.h file not found
的错(如果没有报错,请自动忽略.)
解决办法只需修改Podfile文件,只需pod install即可.
原Podfile文件内容
platform :ios, '9.0'
target 'RealmDemo' do
use_frameworks!
# Pods for RealmDemo
pod 'Realm', '~> 3.0.0-beta.2'
end
修改方案1:(推荐方法)
target 'RealmObjc' do
# Pods for RealmObjc
pod 'Realm', '~> 3.0.0-beta.2'
target 'RealmObjcTests' do
inherit! :search_paths
# Pods for testing
end
end
修改方案2:
platform :ios, '9.0'
target 'RealmDemo' do
use_frameworks!
# Pods for RealmDemo
pod 'Realm', '~> 3.0.0-beta.2'
end
target 'RealmDemoTests' do
use_frameworks!
# Pods for RealmDemoTests
pod 'Realm', '~> 3.0.0-beta.2'
end
3.创建一个继承于RLMObject的model,我们在.h文件中生命对应的属性.
注意:这里生命属性的时候最好不要有(nonatomic,strong)属性的修饰,官网也建议我们不要有修饰,避免出错.
例:官网标准写法
// Define your models like regular Objective‑C classes
interface Dog : RLMObject
property NSString *name;
property NSData *picture;
property NSInteger age;
@end
@implementation Dog
@end
RLM_ARRAY_TYPE(Dog)
所以我们的生命也是如下的写法:
@interface Stu : RLMObject
@property int num;
@property NSString *name;
@end
4.数据库操作
4..1创建模型------增
//方法1:
Stu *stu = [[Stu alloc]initWithValue:@[@2,@"土豆"]];
RLMRealm *realm = [RLMRealm defaultRealm];
[realm beginWriteTransaction];
[realm addObject:stu];
[realm commitWriteTransaction];
//方法2:
Stu *stu = [[Stu alloc]initWithValue:@[@2,@"土豆"]];
RLMRealm *realm = [RLMRealm defaultRealm];
[realm transactionWithBlock:^{
[realm addObject:stu];
}];
//方法3:
RLMRealm *realm = [RLMRealm defaultRealm];
[realm transactionWithBlock:^{
[Stu createInRealm:realm withValue:@{@"num":@3,@"name":@"哈哈"}];
}];
4.2.删除数据 ------ 删
//1.删除所有模型
RLMRealm *realm = [RLMRealm defaultRealm];
//删除的模型,一定要求是被realm所管理的
[realm transactionWithBlock:^{
[realm deleteAllObjects];
}];
//2.删除某条数据
RLMRealm *realm = [RLMRealm defaultRealm];
Stu *stu = [[Stu alloc]initWithValue:@[@2,@"土豆"]];
//删除的模型,一定要求是被realm所管理的
[realm transactionWithBlock:^{
[realm deleteObject:stu];
}];
//3. 删除某一特定类型的所有模型
RLMRealm *realm = [RLMRealm defaultRealm];
RLMResults *stuRes = [Stu allObjects];
for (Stu *stu in stuRes) {
[realm transactionWithBlock:^{
[realm deleteObject:stu];
}];
}
//4. 根据主键 删除一个模型
//1.根据主键,查询到这个模型(这个模型,就是被realm数据库管理的模型)
Stu *stuDel = [Stu objectInRealm:realm forPrimaryKey:@2];
//2.删除该模型
[realm transactionWithBlock:^{
[realm deleteObject:stuDel];
}];
4.3.更新模型属性的值 ------改
//写法1:
- (void)testUpdate{
Stu *stu = [[Stu alloc]initWithValue:@[@2,@"土豆"]];
RLMRealm *realm = [RLMRealm defaultRealm];
//这个模型stu已经被realm管理,而且已经和磁盘上的对象进行了地址映射
[realm transactionWithBlock:^{
[realm addObject:stu];
NSLog(@"num:%d name: %@",stu.num,stu.name);
}];
//这里面修改的模型,一定是被realm所管理的模型
[realm transactionWithBlock:^{
stu.name = @"拉阿拉";
NSLog(@"num:%d name: %@",stu.num,stu.name);
}];
}
//写法2:
RLMResults *results = [Stu objectsWhere:@"name = '拉阿拉'"];
Stu *stu = results.firstObject;
[realm transactionWithBlock:^{
stu.name = @"xxxxxxx拉阿拉xxxxxxxx";
NSLog(@"num:%d name: %@",stu.num,stu.name);
}];
//方法3:
Stu *stu = [[Stu alloc]initWithValue:@[@2,@"土豆"]];
RLMRealm *realm = [RLMRealm defaultRealm];
[realm transactionWithBlock:^{
[realm addOrUpdateObject:stu];
NSLog(@"num:%d name: %@",stu.num,stu.name);
}];
//方法4:
RLMRealm *realm = [RLMRealm defaultRealm];
[realm transactionWithBlock:^{
[Stu createOrUpdateInRealm:realm withValue:@{@"num":@3,@"name":@"哈哈"}];
}];
4.4.查找数据 ------查
方法1:
//查询所有对象
//所有的查询(包括查询和属性访问)在realm中都是延迟加载的,终于当属性被访问时,才能够读取相应的数据
RLMResults *result = [Stu allObjects];
NSLog(@"%@",result);
方法2:
//带条件的查询
RLMResults *result2 = [Stu objectsWhere:@"name = '土豆'"];
NSLog(@"%@",result2);
方法3:
//对查询结果升序降序
RLMResults *sorRes = [result sortedResultsUsingKeyPath:@"name" ascending:YES];
NSLog(@"%@",sorRes);
方法4:
//链式查询 在sorRes的结果集里查找结果.
RLMResults *subRes = [sorRes objectsWhere:@"name = '哈哈'"];
NSLog(@"%@",subRes);
小结:
常见错误总结:
错误1:
一般这种错误的原因都是因为你修改了模型的属性,或者增加了模型的属性字段造成的.
解决办法:详见5.数据库迁移操作--适用于修改了表结构的情况
错误2:
这种错误的原因一般都是因为模型中有必填的字段,而你却没有给该字段赋值造成的.
解决办法:给改字段赋值即可.
小技巧:
realm中如果想要更改数据库的名字,只需要在保存对象前调用一下这个方法即可
- (void)setDefaultRealmForUser:(NSString *)username{
RLMRealmConfiguration *config = [RLMRealmConfiguration defaultConfiguration];
//使用默认的目录,使用用户名来替换默认的文件名
config.fileURL = [[[config.fileURL URLByDeletingLastPathComponent]URLByAppendingPathComponent:username]URLByAppendingPathExtension:@"realm"];
//将这个配置应用到默认的realm数据库中
[RLMRealmConfiguration setDefaultConfiguration:config];
}
5.数据库迁移操作--适用于修改了表结构的情况
5.1数据结构的迁移
操作步骤:
1.创建一个Stu的模型,并声明一个name的属性
@interface Stu : RLMObject
@property NSString *name;
@end
RLMRealm *realm = [RLMRealm defaultRealm];
Stu *stu = [[Stu alloc]initWithValue:@{@"name":@"Allison"}];
[realm transactionWithBlock:^{
[realm addObject:stu];
NSLog(@" ------- name: %@,stu.name);
}];
此时运行数据库中会多一个名字为Allison的数据.
但是如果此时我们修改了Stu的模型,如在模型中加一个@property int age;的字段,修改代码中 *Stu stu = [[Stu alloc]initWithValue:@{@"name":@"Allison",@"age":@20}];,再次运行,会报一下的错误信息
这里提示需要迁移数据,原因是因为表的结构变化了,所以需要迁移数据.
5.2.数据迁移
在APPdelegate里的didFinishLaunchingWithOptions方法里面写数据迁移.
//1.获取默认配置
RLMRealmConfiguration *config = [RLMRealmConfiguration defaultConfiguration];
//2.叠加版本号(要比上一次的版本号高) 默认版本号0
int newVersion = 1;
config.schemaVersion = newVersion;
//3.具体怎样迁移
[config setMigrationBlock:^(RLMMigration *migration, uint64_t oldSchemaVersion){
if (oldSchemaVersion < newVersion) {
NSLog(@"需要做迁移操作");
}
}];
//4.让配置生效
[RLMRealmConfiguration setDefaultConfiguration:config];
//5.如果需要立即生效
[RLMRealm defaultRealm];
这里运行此处的代码,即可完成数据迁移的操作,表结构就会多一个age字段,之前的数据也存在.block中无需做任何事情,就可以完成数据结构和数据的迁移.
但是如果是这样的场景比如说我想合并两列的内容到一列,如fullName = preName + lastName
修改Stu的模型 ,增加preName,lastName和fullName属性.
@interface Stu : RLMObject
@property int age;
@property NSString *name;
@property NSString *preName;
@property NSString *lastName;
@property NSString *fullName;
@end
修改刚代码如下
RLMRealm *realm = [RLMRealm defaultRealm];
Stu *stu = [[Stu alloc]init];
stu.age = 20;
stu.name = @"Allison";
stu.preName = @"XXXXX";
stu.lastName = @"wangjiaojiao";
[realm transactionWithBlock:^{
[realm addObject:stu];
}];
运行数据库中即有preName何lastName数据信息.
修改迁移代码如下
//1.获取默认配置
RLMRealmConfiguration *config = [RLMRealmConfiguration defaultConfiguration];
//2.叠加版本号(要比上一次的版本号高) 0
int newVersion = 9;
config.schemaVersion = newVersion;
//3.具体怎样迁移
[config setMigrationBlock:^(RLMMigration *migration, uint64_t oldSchemaVersion){
if (oldSchemaVersion < newVersion) {
[migration enumerateObjects:@"Stu" block:^(RLMObject * _Nullable oldObject, RLMObject * _Nullable newObject) {
newObject[@"fullName"] = [NSString stringWithFormat:@"%@%@",newObject[@"preName"],newObject[@"lastName"]];
}];
}
}];
//4.让配置生效
[RLMRealmConfiguration setDefaultConfiguration:config];
//5.如果需要立即生效
[RLMRealm defaultRealm];
5.3.属性重命名---更名动作
比如说将刚刚的fullName更名为fullName2
只需要修改block中的代码如下
//执行更名动作
[migration renamePropertyForClass:@"Stu" oldName:@"fullName" newName:@"fullName2"];
5.4.多版本增量式迁移
比如:我们线上的版本1.0,数据库版本1.1,用户A,B.C分别装在手机上了,后期我线上的版本升级到了2.0,数据库版本也升级到了2.0,用户A升级到了2.0,但是用户B,C没有升级,手机上依旧是1.0.这个时候我们又迭代了一个版本3.0,数据库版本3.0,这个时候就是这样子
用户A: 数据库版本 2.0
用户B: 数据库版本 1.0
用户C: 数据库版本 1.0
这个时候数据库版本1.0升级到3.0,和2.0升级到3.0操作是不一样的
所以在升级操作的block中应该分开来操作
if (oldSchemaVersion < 1) {
//迁移第一个版本
}
if (oldSchemaVersion < 2) {
//迁移第二个版本
}
//等等...