Realm-cocoa 学习笔记(一)
一、关于Realm
-
Realm 是用于移动端的数据库,目前支持 iOS 和 Android 平台,它相比于 SQLite 和 CoreData 使用简单,学习成本低,性能更高效。
-
Realm 不是基于Core Data,也不是基于SQLite封装构建的。它有自己的数据库存储引擎。
-
Realm 的 iOS 平台开源项目:Realm-cocoa
-
Objective-C 版本的 Realm 的设计目标包含了能够在 Objective-C 和 Swift 混编的项目中使用。
二、安装
-
在Podfile中,OC工程添加pod 'Realm',Swift工程添加use_frameworks!和pod 'RealmSwift'.(注意需要CocoaPods 0.39.0 或者更高版本)
-
使用插件管理工具 Alcatraz 安装插件 RealmPlugin 重启Xcode后可以看到:
-
App Store下载一个Realm Browser 的软件,用于查看Realm数据库文件。
三、使用
1.数据模型
-
Realm 数据模型必须直接或间接继承于 RLMObject .
-
Realm 模型对象在形式上基本上与其 他 Objective-C 对象相同,可以给它们添加您自己的方法(method)和协议(protocol).
-
Realm支持以下的属性类型:BOOL、bool、int、NSInteger、long、long long、float、double、NSString、NSDate、NSData 以及 被特殊类型标记的 NSNumber.
-
CGFloat 属性不支持
-
由于 Realm 在自己的引擎内部有很好的语义解释系统,所以Objective‑C的许多属性特性将被忽略,
-
如 nonatomic, atomic, strong, copy和weak等。编写数据模型的时候不要使用任何的属性特性。
1.1 Dog 模型的定义
//Dog.h
#import <Realm/Realm.h>
@class Person;
@interface Dog : RLMObject
@property NSInteger dogID;
@property NSString *name;
@property NSData *picture;
@property NSInteger age;
@property NSNumber<RLMFloat> *money;
@property NSString *nationality;
@property NSString *nothing;
@property Person *owner;
@end
//RLM_ARRAY_TYPE 宏创建了一个协议,从而允许 RLMArray<Dog> 语法的使用。
// This protocol enables typed collections. i.e.:
// RLMArray<Dog>
RLM_ARRAY_TYPE(Dog)
//Dog.m
#import "Dog.h"
#import "Person.h"
@implementation Dog
/**
* 重写 +primaryKey 可以设置模型的主键。声明主键之后,对象将被允许查询,更新速度更加高效,并且要求每个对象保持唯一性。
* 带有主键的对象被添加到 Realm 之后,该对象的主键将不可修改。
*/
+ (NSString *)primaryKey{
return @"dogID";
}
/**
* 重写+defaultPropertyValues可以在每次对象创建之后为其提供默认值。
*/
+ (NSDictionary *)defaultPropertyValues
{
return @{@"nationality" : @"Chinese"};
}
/**
* 重写 +ignoredProperties 可以防止 Realm 存储数据模型的某个属性。
*/
+ (NSArray *)ignoredProperties
{
return @[@"nothing"];
}
/**
* 设置非空属性
* 尝试给 name 属性设置为 nil 将会抛出一个异常,但是将 picture 属性设置为 nil 却是允许的
*/
+ (NSArray<NSString *> *)requiredProperties{
return @[@"name"];
}
/**
* 重写 +indexedProperties 方法可以为数据模型中需要添加索引的属性建立索引
* Realm 支持字符串、整数、布尔值以及 NSDate 属性作为索引
*/
+ (NSArray<NSString *> *)indexedProperties{
return @[@"age"];
}
@end
1.2 Person 模型的定义
Person.h
/*
* 使用RLMArray<Object *><Object> 和 RLMObject的子类来建立诸如一对多、一对一之类的关系模型。
* RLMArray: 属性类型 类似于 NSMutableArray
* <Object *>: 属性的特别化(generic specialization),这可以阻止在编译时使用错误对象类型的数组。
* <Object>: 此RLMArray遵守的协议,可以让 Realm 知晓如何在运行时确定数据模型的架构。
* RLMArray 的类型是固定的,其中只能存放简单的 RLMObject 子类类型
*/
#import <Realm/Realm.h>
#import "Dog.h"
@interface Person : RLMObject
@property NSString *name;
@property RLMArray<Dog *><Dog> *dogs;
@end
// This protocol enables typed collections. i.e.:
// RLMArray<Person>
RLM_ARRAY_TYPE(Person)
1.3 Dog 和 Person 模型的关系操作
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
[self test1];
[self test2];
[self test3];
}
- (void)test1{
Dog *mydog = [[Dog alloc] init];
mydog.name = @"大黄";
mydog.dogID = 0001; //主键不为空
mydog.age = 1;
mydog.picture = nil; // 属性的值可以为空
mydog.money = nil;
// 检索 Realm 数据库,找到小于 2 岁 的所有狗狗
RLMResults<Dog *> *puppies = [Dog objectsWhere:@"age < 5"];
NSLog(@"puppies: %ld",puppies.count);// => 0 因为目前还没有任何狗狗被添加到了 Realm 数据库中
//入库
RLMRealm *realm = [RLMRealm defaultRealm];
[realm transactionWithBlock:^{
[realm addObject:mydog];
}];
//检索结果会实时更新
NSLog(@"puppies: %ld",puppies.count);// => 1
// 可以在任何一个线程中执行检索操作
dispatch_async(dispatch_queue_create("background", 0), ^{
//检索
Dog *theDog = [[Dog objectsWhere:@"age == 1"] firstObject];
RLMRealm *realm = [RLMRealm defaultRealm];
//修改属性
[realm beginWriteTransaction];
theDog.age = 3;
[realm commitWriteTransaction];
NSLog(@"name:%@ age:%ld",theDog.name,theDog.age);// => name:大黄 age:3 检索结果会实时更新
});
//主键查询
NSLog(@"dogID=0001:%@",[Dog objectForPrimaryKey:@0001].name);// => dogID=0001:大黄
}
- (void)test2{
//创建
Dog *mydog = [[Dog alloc] init];
mydog.name = @"黑子";
mydog.dogID = 0002;
mydog.age = 10;
mydog.picture = nil;
mydog.owner = nil;
Person *leoo = [[Person alloc] init];
leoo.name = @"leoo";
mydog.owner = leoo;//通过这个属性完成一对一的关系绑定
//入库
RLMRealm *realm = [RLMRealm defaultRealm];
[realm transactionWithBlock:^{
[realm addObject:mydog];
[realm addObject:leoo];
}];
//查询
Dog *heizi = [[Dog objectsWhere:@"name contains '黑子'"] firstObject];
NSLog(@"%@",heizi.owner.name);// => leoo
}
/**
* 可以给 RLMArray 属性赋值为 nil,但是这仅用于“清空”数组,而不是用以移除数组。
* RLMArray 会确保插入 nil 后次序不会被扰乱。
*/
- (void)test3{
Dog *xiaoBai = [[Dog alloc] init];
xiaoBai.name = @"xiaoBai";
xiaoBai.dogID = 0003;
Dog *xiaoHong = [[Dog alloc] init];
xiaoHong.name = @"xiaoHong";
xiaoHong.dogID = 0004;
Person *leoo = [[Person objectsWhere:@"name contains 'leoo'"] firstObject];
NSLog(@"%@ have %ld dog",leoo.name,leoo.dogs.count); // => leoo have 0 dog
RLMRealm *realm = [RLMRealm defaultRealm];
//修改属性 一对多关系
[realm beginWriteTransaction];
/* Person 实例的 dogs 属性添加新的 Dog,并不会自动将狗的 owner 属性设置为该 Person。*/
[leoo.dogs addObjects:@[xiaoHong,xiaoHong]];
[realm commitWriteTransaction];
//检索结果会实时更新
NSLog(@"%@ have %ld dog",leoo.name,leoo.dogs.count);// => leoo have 2 dog
}
1.4 模型类的继承
允许操作
- 父类中的类方法,实例方法和属性可以被它的子类所继承
- 子类中可以在方法以及函数中使用父类作为参数
不允许操作
- 多态类之间的转换(例如子类转换成子类,子类转换成父类,父类转换成子类等)
- 同时对多个类进行检索
- 多类容器 (RLMArray 以及 RLMResults)
官方文档示例:类组合模式
// 基础模型
@end
@interface Animal : RLMObject
@property NSInteger age;
@end
@implementation Animal
@end
// 包含有 Animal 的模型
@interface Duck : RLMObject
@property Animal *animal;
@property NSString *name;
@end
@implementation Duck
@end
@interface Frog : RLMObject
@property Duck *duck;
@property NSDate *dateProp;
@end
@implementation Frog
// 用法
Duck *duck = [[Duck alloc] initWithValue:@{@"animal" : @{@"age" : @(3)}, @"name" : @"Gustav" }];
Frog *frog = [[Frog alloc] initWithValue:@{@"duck":@{@"animal" : @{@"age" : @(3)}, @"name" : @"Gustav" },@"dateProp":[NSDate date]}];
2.集合
- RLMResults类,表示从检索 中所返回的对象集合。
- RLMArray类,表示模型中的对多关系,只能够包含 RLMObject 类型,不能包含诸如NSString之类的基础类型。
- RLMLinkingObjects类,表示模型中的反向关系。
- RLMCollection协议,定义了所有 Realm 集合所需要遵守的常用接口。