必威-必威-欢迎您

必威,必威官网企业自成立以来,以策略先行,经营致胜,管理为本的商,业推广理念,一步一个脚印发展成为同类企业中经营范围最广,在行业内颇具影响力的企业。

Realm数据模型是基于标准 Objective‑C,令人惊叹

2019-09-17 13:22 来源:未知
// jim 是 rex 以及所有名字叫“Fido”的狗狗的主人RLMResults<Dog *> *someDogs = [Dog objectsWhere:@"name contains 'Fido'"];[jim.dogs addObjects:someDogs];[jim.dogs addObject:rex];

RLM_ARRAY_TYPE//定义RLMArray

使用教程:

Realm 数据库


链式查询

Realm查询引擎的一个独特属性就是它能够进行简单快捷的链式查询, 而不需要像传统数据库一样的麻烦。举个例子来说,假如你要所有黄褐色的小狗的结果序列,然后从中查找名字开头是“B“的小狗。 你可以发送如下的请求。<code>RLMResults *tanDogs = [Dog objectsWhere:@"color = 'tan'"];RLMResults *tanDogsWithBNames = [tanDogs objectsWhere:@"name BEGINSWITH 'B'"];</code>

实用例

#import <Realm/Realm.h>@interface BKUserPayWay : RLMObject@property NSString *userPayWayName;@property NSString *userPayWayValue;@endRLM_ARRAY_TYPE(BKUserPayWay) //定义RLMArray< BKUserPayWay >@interface BKUserStatusInfo : RLMObject@property NSString * userNum;@property NSString * userPwd;@property NSString * deveiceToken;//有糖小店支付 信息@property NSString * userSecretKey;@property NSString * userValidTime;@property RLMArray <BKUserPayWay *><BKUserPayWay>* userPayWays;

链接是单向性的。因此,如果对多关系属性 Person.dogs 链接了一个 Dog 实例,而这个实例的对一关系属性 Dog.owner 又链接到了对应的这个 Person 实例,那么实际上这些链接仍然是互相独立的。为 Person 实例的 dogs 属性添加一个新的 Dog 实例,并不会将这个 Dog 实例的 owner 属性自动设置为该 Person 。但是由于手动同步双向关系会很容易出错,并且这个操作还非常得复杂、冗余,因此 Realm 提供了 “链接对象 (linking objects)” 属性来表示这些反向关系。

借助链接对象属性,您可以通过指定的属性来获取所有链接到指定对象的对象。例如,一个 Dog 对象可以拥有一个名为 owners 的链接对象属性,这个属性中包含了某些 Person 对象,而这些 Person 对象在其 dogs 属性中包含了这一个确定的 Dog 对象。您可以将 owners 属性设置为 RLMLinkingObjects 类型,然后重写 +[RLMObject linkingObjectsProperties] 来指明关系,说明 ownders 中包含了 Person 模型对象。

@interface Dog : RLMObject@property NSString *name;@property NSInteger age;@property  RLMLinkingObjects *owners;@end@implementation Dog+ (NSDictionary *)linkingObjectsProperties { return @{ @"owners": [RLMPropertyDescriptor descriptorWithClass:Person.class propertyName:@"dogs"], };}@end

通常情况下,NSString *NSData * 以及 NSDate * 属性可以设置为 nil。如果你不需要实现此功能,你可以重写您的 RLMObject 子类的 +requiredProperties 方法。

比如对于以下的模型定义来说,如果尝试给 name 属性设置为 nil将会抛出一个异常,但是将 birthday 属性设置为 nil却是允许的:

@interface Person : RLMObject@property NSString *name;@property NSDate *birthday;@end@implementation Person+ (NSArray *)requiredProperties { return @[@"name"];}@end

存储可空数字目前已经可以通过 NSNumber * 属性完成。由于 Realm 对不同类型的数字采取了不同的存储格式,因此设置可空的数字属性必须是 RLMIntRLMFloatRLMDouble 或者 RLMBool 类型。所有赋给属性的值都会被转换为其特定的类型。

请注意:NSDecimalNumber 的值只能分配给类型为 RLMDouble 的 Realm 属性,此外 Realm 将会存储近似于双精度浮点的数值,而不是存储基本的十进制数值。

比如说,如果我们存储一个用户的年龄而不是存储他们的生日,同时还要允许当您不知道该用户的年龄的时候将 age 属性设置为 nil

@interface Person : RLMObject@property NSString *name;@property NSNumber<RLMInt> *age;@end@implementation Person+ (NSArray *)requiredProperties { return @[@"name"];}@end

RLMProperty 的子类属性始终都可以为 nil,因此这些类型不能够放在 requiredProperties 中,并且 RLMArray 不支持存储 nil 值。

这个表格提供了关于声明模型属性的简易参考:

类型 非可选值形式 可选值形式
Bool @property BOOL value; @property NSNumber<RLMBool> *value;
Int @property int value; @property NSNumber<RLMInt> *value;
Float @property float value; @property NSNumber<RLMFloat> *value;
Double @property double value; @property NSNumber<RLMDouble> *value;
String @property NSString *value; @property NSString *value;
Data @property NSData *value; @property NSData *value;
Date @property NSDate *value; @property NSDate *value;
Object n/a: 必须是可选值 @property Object *value;
List @property RLMArray<Object *><Object> *value; n/a: 必须是非可选值
LinkingObjects @property RLMLinkingObjects<Object *> *value; n/a: 必须是非可选值
  1. Objective‑C 引用类型的必需属性必须要声明在联合体当中:
@implementation MyModel+ (NSArray *)requiredProperties { return @[@"value"];}@end
  1. 链接对象属性必须连带 +linkingObjectsProperties 方法一同声明:
@implementation MyModel+ (NSDictionary *)linkingObjectsProperties { return @{ @"property": [RLMPropertyDescriptor descriptorWithClass:Class.class propertyName:@"link"] };}@end

注意由于 Realm 在自己的引擎内部有很好的语义解释系统,所以 Objective‑C 的许多属性特性将被忽略,如 nonatomic, atomic, strong, copyweak 等。 因此为了避免误解,我们推荐您在编写数据模型的时候不要使用任何的属性特性。 当然,如果您已经设置了这些属性特性,那么在 RLMObject 对象被写入 Realm 数据库前,这些特性会一直生效。 无论 RLMObject 对象是否受到 Realm 管理,您为其编写的自定义 gettersetter 方法都能正常工作。如果您在 Swift 中使用 Objective-C 版本的 Realm 的话,模型的属性前面需要加上 dynamic var,这是为了让这些属性能够被底层数据库数据所访问。

重写 +indexedProperties 方法可以为数据模型中需要添加索引的属性建立索引:

@interface Book : RLMObject@property float price;@property NSString *title;@end@implementation Book+ (NSArray *)indexedProperties { return @[@"title"];}@end

Realm 支持字符串、整数、布尔值以及 NSDate 属性作为索引。对属性进行索引可以减少插入操作的性能耗费,加快比较检索的速度(比如说 = 以及 IN 操作符)。

重写+defaultPropertyValues 可以每次在对象创建之后为其提供默认值。

@interface Book : RLMObject@property float price;@property NSString *title;@end@implementation Book+ (NSDictionary *)defaultPropertyValues { return @{@"price" : @0, @"title": @""};}@end

RLMObject 实例是底层数据的动态表现,其会进行自动更新,这意味着对象不需要进行刷新。修改某个对象的属性会立刻影响到其他所有指向同一个对象的实例。

Dog *myDog = [[Dog alloc] init];myDog.name = @"小白";myDog.age = 1;[realm transactionWithBlock:^{ [realm addObject:myDog];}];Dog *myPuppy = [[Dog objectsWhere:@"age == 1"] firstObject];[realm transactionWithBlock:^{ myPuppy.age = 2;}];myDog.age; // => 2

RLMObject 的这个特性不仅让 Realm 保证速度和效率,它同时还让代码更加简洁、更为灵活。比如说,如果您的 UI 代码是基于某个特定的 Realm 对象来现实的,那么在触发 UI 重绘之前,您不用担心数据的刷新或者重新检索等问题。您也可以查看 Realm 通知 一节以确认 Realm 数据何时被更新,比如说由此来决定应用 UI 何时需要被更新。此外,还可以使用 键值编码,当某个 RLMObject 的特定属性发生更新时去发送通知。

重写 +primaryKey 可以设置模型的主键。声明主键之后,对象将允许进行查询,并且更新速度更加高效,而这也会要求每个对象保持唯一性。 一旦带有主键的对象被添加到 Realm 之后,该对象的主键将不可修改。

@interface Person : RLMObject@property NSInteger id;@property NSString *name;@end@implementation Person+ (NSString *)primaryKey { return @"id";}@end

重写 +ignoredProperties 可以防止 Realm 存储数据模型的某个属性。Realm 将不会干涉这些属性的常规操作,它们将由成员变量提供支持,并且您能够轻易重写它们的 settergetter

@interface Person : RLMObject@property NSInteger tmpID;@property  NSString *name; // 只读属性将被自动忽略@property NSString *firstName;@property NSString *lastName;@end@implementation Person+ (NSArray *)ignoredProperties { return @[@"tmpID"];}- (NSString *)name { return [NSString stringWithFormat:@"%@ %@", self.firstName, self.lastName];}@end

忽略属性的行为与 Objective-C 或者 Swift 类当中的普通对象相似。它们并不支持任何一种 Realm 特定的功能。例如,无法通过查询来检索忽略属性,也无法实现自动更新,即便另一个相同对象的实例的忽略属性值发生了变更。此外忽略属性发生更改的时候也不会触发通知,尽管仍然可以使用 KVO 来实现简直观察。

Realm 允许模型能够生成更多的子类,也允许跨模型进行代码复用,但是由于某些 Cocoa 特性使得运行时中丰富的类多态无法使用。以下是可以完成的操作:父类中的类方法,实例方法和属性可以被它的子类所继承子类中可以在方法以及函数中使用父类作为参数

以下是不能完成的:多态类之间的转换(例如子类转换成子类,子类转换成父类,父类转换成子类等)同时对多个类进行检索多类容器 (RLMArray 以及 RLMResults)。

向 Realm 中增加此特性已经在规划当中,并且我们暂时提供了一些代码示例,以便能够对更常见的模式进行处理。

另外,如果您的代码实现允许的话,我们建议您使用以下模式,也就是使用类组合模式来构建子类,以便能够包含其他类中的相关逻辑:

// 基础模型@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 Animal *animal;@property NSDate *dateProp;@end@implementation Frog@end// 用法Duck *duck = [[Duck alloc] initWithValue:@{@"animal" : @{@"age" : @}, @"name" : @"Gustav" }];

Realm 拥有一系列能够帮助表示一组对象的类型,我们称之为「Realm 集合」:1、RLMResults类,表示从检索 中所返回的对象集合。2、RLMArray类,表示模型中的对多关系。3、RLMLinkingObjects类,表示模型中的反向关系。4、RLMCollection 协议,定义了所有 Realm 集合所需要遵守的常用接口。

Realm 集合实现了 RLMCollection 协议,这确保它们能够保持一致。这个协议继承自 NSFastEnumeration,因此它应当与其他 Foundation 当中的集合用法一致。 其他常用的 Realm 集合 API 也在这个协议当中进行了声明,例如其中包括检索、排序以及聚合操作。 RLMArray 拥有额外的修改操作,这些操作不在协议接口当中有定义,例如添加和删除对象。使用 RLMCollection 协议,您可以编写能够操作任意 Realm 集合的泛型代码:

@implementation MyObject- operateOnCollection:(id<RLMCollection>)collection { // collection 既可以是 RLMResults,也可以是 RLMArray NSLog(@"对集合 %@s 进行操作", collection.objectClassName);}@end

对对象的所有更改都必须通过写入事务(transaction)完成。

Realm 的对象可以被实例化并且作为unmanaged对象使用(也就是还未添加到 Realm 数据库中的对象),和其他常规Objective‑C对象无异。如果您想要在多个线程中共享对象,或者在应用重启后重复使用对象,那么您必须将其添加到 Realm 数据库中——这个操作必须在写入事务中完成。因为写入事务将会产生不可忽略的性能消耗,因此你应当检视你的代码以确保减少写入事务的次数。由于写入事务像其余硬盘读写操作一样,会出现失败的情况,因此 -[RLMRealm transactionWithBlock:] 以及 -[RLMRealm commitWriteTransaction] 可以选择加上 NSError 指针参数 因此你可以处理和恢复诸如硬盘空间溢出之类的错误。此外,其他的错误都无法进行恢复。简单起见,我们的代码示例并不会处理这些错误,但是您应当在您应用当中注意到这些问题。

当定义完数据模型之后,您可以将您的 RLMObject 子类实例化,然后向 Realm 中添加新的实例。我们以下面这个简单的模型为例:

// 狗狗的数据模型@interface Dog : RLMObject@property NSString *name;@property NSInteger age;@end// 实现文件@implementation Dog@end

我们可以用多种方法创建一个新的对象:

//  创建一个狗狗对象,然后设置其属性Dog *myDog = [[Dog alloc] init];myDog.name = @"大黄";myDog.age = 10;//  通过字典创建狗狗对象Dog *myOtherDog = [[Dog alloc] initWithValue:@{@"name" : @"豆豆", @"age" : @3}];//  通过数组创建狗狗对象Dog *myThirdDog = [[Dog alloc] initWithValue:@[@"豆豆", @3]];

使用指定初始化器(designated initializer)创建对象是最简单的方式。请注意,所有的必需属性都必须在对象添加到 Realm 前被赋值。通过使用恰当的键值,对象还可以通过字典完成创建。最后,RLMObject 子类还可以通过数组完成实例化,数组中的值必须和数据模型中对应属性的次序相同。

如果某个对象中有 RLMObject 或者 RLMArray 类型的属性,那么通过使用嵌套的数组或者字典便可以对这些属性递归地进行设置。您只需要简单的用表示其属性的字典或者数组替换每个对象即可:

// 这里我们就可以使用已存在的狗狗对象来完成初始化Person *person1 = [[Person alloc] initWithValue:@[@"李四", @30, @[aDog, anotherDog]]];// 还可以使用多重嵌套Person *person2 = [[Person alloc] initWithValue:@[@"李四", @30, @[@[@"小黑", @5], @[@"旺财", @6]]]];

即使是数组以及字典的多重嵌套,Realm 也能够轻松完成对象的创建。注意 RLMArray 只能够包含 RLMObject 类型,不能包含诸如 NSString之类的基础类型。

向 Realm 中添加数据的步骤如下:

// 创建对象Person *author = [[Person alloc] init];author.name = @"大卫·福斯特·华莱士";// 获取默认的 Realm 实例RLMRealm *realm = [RLMRealm defaultRealm];// 每个线程只需要使用一次即可// 通过事务将数据添加到 Realm 中[realm beginWriteTransaction];[realm addObject:author];[realm commitWriteTransaction];

等您将某个对象添加到 Realm 数据库之后,您可以继续使用它,并且您对其做的任何更改都会被保存(必须在一个写入事务当中完成)。当写入事务提交之后,使用相同 Realm 数据源的其他线程才能够对这个对象进行更改。

请注意:如果在进程中存在多个写入操作的话,那么单个写入操作将会阻塞其余的写入操作,并且还会锁定该操作所在的当前线程。这个特性与其他持久化解决方案类似,我们建议您使用该方案常规的最佳做法:将写入操作转移到一个独立的线程中执行。

由于 Realm 采用了 MVCC 设计架构,读取操作 并不会 因为写入事务正在进行而受到影响。除非您需要立即使用多个线程来同时执行写入操作,不然您应当采用批量化的写入事务,而不是采用多次少量的写入事务。查看RLMRealm和RLMObject来获得更多内容。

Realm 提供了一系列用以更新数据的方式,这些方式都有着各自所适应的情景。请选择最符合您当前需求的方式来使用:

您可以在写入事务中通过设置某个对象的属性从而完成对象的更新操作。

// 在一个事务中更新对象[realm beginWriteTransaction];author.name = @"托马斯·品钦";[realm commitWriteTransaction];

如果您的数据模型中设置了主键的话,那么您可以使用+[RLMObject createOrUpdateInRealm:withValue:]来更新对象,或者当对象不存在时插入新的对象。

// 创建一个带有主键的“书籍”对象,作为事先存储的书籍Book *cheeseBook = [[Book alloc] init];cheeseBook.title = @"奶酪食谱";cheeseBook.price = @9000;cheeseBook.id = @1;// 通过 id = 1 更新该书籍[realm beginWriteTransaction];[Book createOrUpdateInRealm:realm withValue:cheeseBook];[realm commitWriteTransaction];

如果主键 id 为1的 Book 对象已经存在于数据库当中了,那么对象就会简单地进行更新。而如果不在数据库中存在的话,那么这个操作将会创建一个新的 Book 对象并添加到数据库当中。您同时通过传递您想要更新值的集合,从而更新带有主键的某个对象的部分值,比如说如下所示:

// 假设带有主键值 `1` 的“书籍”对象已经存在[realm beginWriteTransaction];[Book createOrUpdateInRealm:realm withValue:@{@"id": @1, @"price": @9000.0f}];// 这本书的`title`属性不会被改变[realm commitWriteTransaction];

如果对象没有定义主键的话,那么您不能够调用出现在本章的这些方法(也就是那些以 OrUpdate 结尾的方法)。请注意:当对象更新的时候,NSNull 仍然会被认为是可选属性 的有效值。如果您提供了一个属性值为 NSNull 的字典,那么这些都会应用到您的对象当中,并且对应的属性都将为空。为了确保不出现任何意外的数据丢失,请在使用此方法的时候再三确认只提供了您想要进行更新的属性。

RLMObjectRLMResult 以及 RLMArray 都遵守键值编码(Key-Value Coding)机制。 当您在运行时才能决定哪个属性需要更新的时候,这个方法是最有用的。将 KVC 应用在集合当中是大量更新对象的极佳方式,这样就可以不用经常遍历集合,为每个项目创建一个访问器了。

RLMResults<Person *> *persons = [Person allObjects];[[RLMRealm defaultRealm] transactionWithBlock:^{ [[persons firstObject] setValue:@YES forKeyPath:@"isFirst"]; // 将每个人的 planet 属性设置为“地球” [persons setValue:@"地球" forKeyPath:@"planet"];}];

通过在写入事务中将要删除的对象传递给 -[RLMRealm deleteObject:] 方法,即可完成删除操作。

// 存储在 Realm 中的 cheeseBook 对象// 在事务中删除一个对象[realm beginWriteTransaction];[realm deleteObject:cheeseBook];[realm commitWriteTransaction];

您也能够删除存储在 Realm 中的所有数据。注意,Realm 文件的大小不会被改变,因为它会保留空间以供日后快速存储数据。

// 从 Realm 中删除所有数据[realm beginWriteTransaction];[realm deleteAllObjects];[realm commitWriteTransaction];

二 更新数据

 

使用


主要使用就是这两个文件, 具体在网络请求以及在什么时候获取/删除缓存数据就不详细说明, 我也在一步一步探索尝试
RNRealmProvider.swift

//
//  RNRealmProvider.swift
//  SpecialHoyoServicer
//
//  Created by roni on 2017/7/5.
//  Copyright © 2017年 roni. All rights reserved.
//

import Foundation
import RealmSwift


//MARK: - Realm 中间层


//MARK: - 增

/// 写事务: 单个对象 -- 也可以通过主键更新整个对象
///
/// - Parameters:
///   - object: 写入对象
///   - isPrimaryKey: 是否有主键  -- 不能够给未定义主键的对象类型传递 update: true
internal func realmWirte(model object: Object, isPrimaryKey: Bool = false) {

    let realm = try! Realm() // 获取默认的 realm 实例

    do {

        try realm.write {
            realm.add(object, update: isPrimaryKey) // 写事务, 将数据添加到 realm 数据库中....
        }
    }
    catch let error {

        print("realm 写入错误: (error.localizedDescription) and Objetct: (object)")
    }

}

/// 写事务: 多个对象 -- 也可以通过主键更新整个对象
///
/// - Parameters:
///   - objects: 写入对象数据
///   - isPrimaryKey: 是否有主键  -- 不能够给未定义主键的对象类型传递 update: true
internal func realmWirteModels(models objects: [Object], isPrimaryKey: Bool = false) {

    let realm = try! Realm() // 获取默认的 realm 实例

    do {

        try realm.write {
            realm.add(objects, update: isPrimaryKey) // 写事务, 将数据添加到 realm 数据库中....
        }
    }
    catch let error {

        print("realm 写入错误: (error.localizedDescription) and Objetct: (objects)")
    }

}



//MARK: - 删

/// 删除单个对象
///
/// - Parameters:
///   - Obj: 类
///   - condition: 满足条件的对象
internal func realmDeleteObject<T: Object>(Model Obj: T.Type, condition: (Results<T>) -> T?) {

    let realm = try! Realm() // 获取默认的 realm 实例

    let results = realm.objects(Obj)

    let model = condition(results)
    guard let m = model else {
        print("没找到符合条件的对象")
        return
    }

    do {

        try realm.write {

            realm.delete(m)
        }
    }
    catch let error {

        print("realm 删除错误: (error.localizedDescription)")
    }

}

/// 删除单个对象
///
/// - Parameters:
///   - Obj: 类
///   - condition: 满足条件的对象
internal func realmDeleteObjects<T: Object>(Model Obj: T.Type, condition: (Results<T>) -> Results<T>?) {

    let realm = try! Realm() // 获取默认的 realm 实例

    let results = realm.objects(Obj)

    let models = condition(results)
    guard let m = models else {
        print("没找到符合条件的对象")
        return
    }

    do {

        try realm.write {

            realm.delete(m)
        }
    }
    catch let error {

        print("realm 删除错误: (error.localizedDescription)")
    }

}


/// 删除所有 -- 慎用
internal func realmDeleteAll() {

    let realm = try! Realm() // 获取默认的 realm 实例

    do {

        try realm.write {

            realm.deleteAll()
        }
    }
    catch let error {

        print("realm 删除错误: (error.localizedDescription)")
    }

}


//MARK: - 改

/// 主键更新
///
/// - Parameters:
///   - Obj: 需要更新的类
///   - value: 需要更新的字段
///   - isPrimaryKey: 是否有主键
internal func realmUpdate<T: Object>(Model Obj: T.Type, value: [String: Any], isPrimaryKey: Bool = false) {

    let realm = try! Realm() // 获取默认的 realm 实例

    do {

        try realm.write {
            realm.create(Obj, value: value, update: isPrimaryKey)
        }
    }
    catch let error {

        print("realm 更新错误: (error.localizedDescription)")
    }

}

/// 键值更新 -- 只针对有主键的 model
///
/// - Parameters:
///   - Obj: 需要更新的类
///   - value: 需要更新的  k-v
///   - isPrimaryKey: 主键
internal func realmUpdateByKVC<T: Object>(Model Obj: T.Type, value: [String: Any], primaryKey: Any) {

    let realm = try! Realm() // 获取默认的 realm 实例

    let model = realm.object(ofType: Obj, forPrimaryKey: primaryKey)

    guard let m = model else {
        print("没找到符合条件的对象")
        return
    }

    do {


        try realm.write {

            for (k, v) in value {
                m.setValue(v, forKey: k)
            }
        }
    }
    catch let error {

        print("realm 更新错误: (error.localizedDescription)")
    }

}

/// 键值更新
///
/// - Parameters:
///   - Obj: 需要更新的类
///   - value: 需要更新的 k-v
///   - condition: 满足条件的对象
internal func realmUpdateByKVCForResults<T: Object>(Model Obj: T.Type, value: [String: Any], condition: (Results<T>) -> T?) {

    let realm = try! Realm() // 获取默认的 realm 实例

    let results = realm.objects(Obj)

    let model = condition(results)
    guard let m = model else {
        print("没找到符合条件的对象")
        return
    }

    do {

        try realm.write {

            for (k, v) in value {
                m.setValue(v, forKey: k)
            }
        }
    }
    catch let error {

        print("realm 更新错误: (error.localizedDescription)")
    }

}


//MARK: - 查

/// 查询对象数组
///
/// - Parameter Obj: 查询的类
/// - Returns: 数组
internal func realmQueryResults<T: Object>(Model Obj: T.Type) -> Results<T> {

    let realm = try! Realm() // 获取默认的 realm 实例

    return realm.objects(Obj)
}

/// 查询对象
///
/// - Parameters:
///   - Obj: 查询的类
///   - condition: 满足条件
/// - Returns: models
internal func realmQueryModel<T: Object>(Model Obj: T.Type, condition: (Results<T>) -> Results<T>?) -> Results<T>? {

    let realm = try! Realm() // 获取默认的 realm 实例

    let results = realm.objects(Obj)

    return condition(results)
}

RNRealmMigration.swift

import Foundation
import RealmSwift


// 数据库迁移文件
// 每次更新之后修改!!!!! 特别是发布前!!!!!!!!, 如果有 model 属性的变更必须修改

/**
 * ## 说明 --- [文档](https://realm.io/cn/docs/swift/latest/#migration)
 * 新增删除表,Realm不需要做迁移
 * 新增删除字段,Realm不需要做迁移, 但需要更改版本号schemaVersion。Realm 会自行检测新增和需要移除的属性,然后自动更新硬盘上的数据库架构 ---
 * <#item2#>
 * <#item3#>
 */


internal func realmMigration() {

    var config = Realm.Configuration(

        schemaVersion: 1, // 设置新的架构版本。这个版本号必须高于之前所用的版本号(如果您之前从未设置过架构版本,那么这个版本号设置为 0)

        // 设置闭包,这个闭包将会在打开低于上面所设置版本号的 Realm 数据库的时候被自动调用
        migrationBlock: { migration, oldSchemaVersion in
            if (oldSchemaVersion < 1) {

              //  migration.enumerateObjects(ofType: <#T##String#>, <#T##block: (MigrationObject?, MigrationObject?) -> Void##(MigrationObject?, MigrationObject?) -> Void#>) // 值更新

             //   migration.renameProperty(onType: <#T##String#>, from: <#T##String#>, to: <#T##String#>) // 重命名属性
            }
    })

    config.fileURL = config.fileURL!.deletingLastPathComponent()
        .appendingPathComponent("hoyoservicer-v(config.schemaVersion).realm")
    Realm.Configuration.defaultConfiguration = config // 告诉 Realm 为默认的 Realm 数据库使用这个新的配置对象

    // 现在我们已经告诉了 Realm 如何处理架构的变化,打开文件之后将会自动执行迁移
    let _ = try! Realm()
}
纯内存数据库

正常的Realm数据库是存储在硬盘上的, 但你也可以通过使用[RLMRealm inMemoryRealmWithIdentifier:]来创建一个纯内存数据库。<code>RLMRealm *realm = [RLMRealm inMemoryRealmWithIdentifier:@"MyInMemoryRealm"];</code>纯内存数据库在每次程序退出时不会保存数据。但是,这不会妨碍到realm的其他功能,包括请求,关系和线程安全。假如你需要灵活的数据读写但又不想永久储存,那么纯内存数据库对你来说一定大有裨益。注意: 如果某个纯内存Realm实例没有被引用,所有的数据就会被释放。强烈建议你在app中用强引用来钳制所有新建的纯内存Realm数据库。

如果要给我们的 Person 数据模型添加一个 “dogs” 属性,以便能够和多个 “dogs” 建立关系,也就是表明一个「人」可以养多条「狗」,那么我们首先需要定义一个 RLMArray<Dog> 类型。通过对应数据模型接口文件下的宏命令即可完成:

反向关系(Inverse Relationship)

据官方称性能上比sqlite, coredata牛逼,而且使用起来更加简单, 更易入门。

Realm 配置

通过Realm.Configuration您可以配置诸如 Realm 文件在何处存储之类的信息
例如为不同用户创建不同的数据库

func setDefaultRealmForUser(username: String) {
  var config = Realm.Configuration()

  // 使用默认的目录,但是使用用户名来替换默认的文件名
  config.fileURL = config.fileURL!.deletingLastPathComponent()
                         .appendingPathComponent("(username).realm")

  // 将这个配置应用到默认的 Realm 数据库当中
  Realm.Configuration.defaultConfiguration = config
}
跨线程使用数据库

在不同的线程间使用同一个Realm数据库,你必须呼叫[RLMRealm defaultRealm],[RLMRealm realmWithPath:]或者 [RLMRealm realmWithPath:readOnly:eror:]以得到个线程相对应的realm对象。只要你路径一致,所有的RLMRealm对象指向的文件就一致。我们还支持跨线程共享RLMRealm对象。

// RLMArray: 属性类型。// <Object *>: 属性的特别化(generic specialization),这可以阻止在编译时使用错误对象类型的数组。// <Object>: 此RLMArray遵守的协议,可以让 Realm 知晓如何在运行时确定数据模型的架构。

tanDogs=[DogobjectsWithPredicate:pred];

2、Xcode插件:可以快速创建Realm可存储模型对象。

创建对象

// (1) 创建一个狗狗对象,然后设置其属性
var myDog = Dog()
myDog.name = "大黄"
myDog.age = 10

// (2) 通过字典创建狗狗对象
let myOtherDog = Dog(value: ["name" : "豆豆", "age": 3])

// (3) 通过数组创建狗狗对象
let myThirdDog = Dog(value: ["豆豆", 5])
  • 使用指定初始化器(designated initializer)创建对象是最简单的方式。
  • 通过使用恰当的键值,对象还可以通过字典完成创建。
  • 最后,Object 子类还可以通过数组完成实例化,数组中的值必须和数据模型中对应属性的次序相同
静态库安装 (Objective-C & Swift)

1.下载最新的Realm发行版本并在本地解压。2.从 ios/static/目录里,把Realm.framework文件拖动到你的Xcode开发项目里的File Navigator 中。 确保Copy items into destination group’s folder已经被选中,按Finish。3.在Xcode file explorer中选中你要的开发项目. 选择target,点击Build Phases选项. 在Link Binary with Libraries里按+,添加libc++.dylib。4.如果使用Realm + Swift,拖动Swift/RLMSupport.swift到你的Xcode projectFile Navigator中,点选Copy items if needed。5.如果在OSX项目中使用Realm,点击左上角的

  • ,选择New Copy Files Phase,将其重命名为Copy Frameworks, 将Destination设置为Frameworks,并且添加Realm.framework
// Dog.h@interface Dog : RLMObject// 其余属性声明...@property Person *owner;@end

四 查询

1.简单的数据操作

说明


内容全部来自文档...只做个人笔记,如需学习请移步官方网站(地址见上面)

当前的限制

realm现在还是beta版本。我们还在为1.0的发布一直不断的添加新特性,修复bug。我们整理了一些普遍存在的限制如果你想要看到完整地issue列表, 参见GitHub issues。

Person *jim = [[Person alloc] init];Dog *rex = [[Dog alloc] init];rex.owner = jim;

在 Xcode 7 以及之后的版本中,RLMArray支持编译时的 Objective-C 泛型。下面是不同属性定义方法的意义以及用途:

下载地址:

内存数据库

通常情况下,Realm 数据库是存储在硬盘中的,但是您能够通过设置 inMemoryIdentifier 而不是设置Realm.Configuration 中的 fileURL 属性,以创建一个完全在内存中运行的数据库。

let realm = try! Realm(configuration: Realm.Configuration(inMemoryIdentifier: "MyInMemoryRealm"))

内存数据库在每次程序运行期间都不会保存数据。但是,这不会妨碍到 Realm 的其他功能,包括查询、关系以及线程安全。 假如您需要灵活的数据读写但又不想储存数据的话,那么内存数据库对您来说一定是一个不错的选择。

内存数据库会在临时文件夹中创建多个文件,用来协调处理诸如跨进程通知之类的事务。 实际上没有任何的数据会被写入到这些文件当中,除非操作系统由于内存过满需要清除磁盘上的多余空间。

注意: 当所有具备特定标识符的Realm 内存数据库实例超出可用范围,但是却没有被引用的话,那么_ Realm 内存数据库中的所有数据都会被删除_。我们建议您在应用的生命周期内,保持对 Realm 内存数据库的强引用。(对于存储在硬盘当中的 Realm 数据库而言,这并不是必须的操作。)

后台操作

通过把大量的写入放入到一个大型事务中,Realm可以大大的提高大型数据读取的运行效率。事务可以在后台通过GCD运行,这样可以避免阻塞主进程。RLMRealm并不是线程安全的,所以你必须在每一个你需要读写的进程或者调度队列中添加RLMRealm实例。这里有一个在后台队列中添加百万级数据的例子。

dispatch_async(queue, ^{ // Get realm and table instances for this thread RLMRealm *realm = [RLMRealm defaultRealm]; // Break up the writing blocks into smaller portions // by starting a new transaction for (NSInteger idx1 = 0; idx1 < 1000; idx1++) { [realm beginWriteTransaction]; // Add row via dictionary. Property order is ignored. for (NSInteger idx2 = 0; idx2 < 1000; idx2++) { [Person createInRealm:realm withObject:@{@"name":[self randomString],@"birthdate":[self randomDate]}]; } // Commit the write transaction // to make this data available to other threads [realm commitWriteTransaction]; }});

可以参考RLMRealm。

```objc// Person.h@interface Person : RLMObject// 其余的属性声明...@property RLMArray<Dog *><Dog> *dogs;@end

1 使用 Realm 构建应用的基本要求:iOS >= 7, OS X >= 10.9 并且支持 WatchKit

realm是一个跨平台移动数据库引擎,支持iOS、OS X(Objective-C和Swift)以及Android;

查询


  • 条件查询
  • 排序
  • 链式查询
  • 自动更新

Results 是底层数据的动态表现,其会进行自动更新,这意味着检索到的结果不能进行重复检索。它们会反映出当前线程上 Realm 的当前状态,包括在当前线程上的写事务当中。唯一的例外是,当使用 for...in 遍历时,因为当遍历开始的时候,总会全部遍历完所有匹配该检索的对象,即使某些对象在遍历过程中被过滤器修改或者删除

let puppies = realm.objects(Dog).filter("age < 2")
puppies.count // => 0
try! realm.write {
  realm.create(Dog.self, value: ["name": "大黄", "age": 1])
}
puppies.count // => 1
其他的realm数据库

有的时候拥有多个分离的数据库是非常有必要的。例如,如果你需要绑定数据到一个App,你可以打开另一个只读的Realm数据库,参考[RLMRealm realmWithPath:][RLMRealm realmWithPath:readOnly:error:]请注意传递到[RLMRealm realmWithPath:]的路径必须是有写入权限的。存储可写Realm数据库的最常用路径就是iOS的“Documents”和OSX的“Application Support”。

这里需要强调的是嵌套数据结构和数据关系的使用,即对 RLMArray 的使用。RLMArray 是Realm的数组,只能存放对象类型的数据。在使用 RLMArray 时需要注意:RLM_ARRAY_TYPE 宏创建了一个协议,从而允许 RLMArray<Dog> 语法的使用。如果该宏没有放置在模型接口的底部的话,您或许需要提前声明该模型类。

我们使用这个类 在ViewController 里面的 viewDidLoad 方法里面 写下面的代码

2.支持的数据类型

支持的类型包括:BOOL, bool, int, NSInteger, long, long long, float, double, NSString, NSDate, NSData, NSNumber。

缺点是不支持集合类型,其解决方案是 序列化成NSData进行存储  或者 *转换成RLMArray<RLMObject>进行存储*

 

对象存储


对对象的所有更改(添加,修改和删除)都必须通过写入事务(transaction)完成。

  • Realm 写入操作是同步以及阻塞的,并不会异步运行

存储对象

对对象的所有更改(添加,修改 和删除)都必须通过写入事务完成。Realm的对象可以被实例化并且被单独使用,和其他常规对象无异。如果你想要在多个线程中共享或者永久保存以重复使用对象,你必须将其存储到Realm数据库中——这个操作必须在写事务中完成。 你可以参照如下代码添加一个对象:创建一个对象Person *author = [[Person alloc] init]; author.name = @"David Foster Wallace";获取一个默认realm对象RealmRLMRealm *realm = [RLMRealm defaultRealm];你只须这么做一次 Add to Realm with transaction<code>[realm beginWriteTransaction];[realm addObject:author];[realm commitWriteTransaction];</code>等到你把这个对象添加到realm数据库里面之后, 你可以在多个线程里面共享之。并且从现在开始,你所做的每一次更改(必须在一个写事务中完成)也会被永久储存。等到写事务完成,这个更改将对所有共享这个Realm数据库的线程可见。需要注意的是,写入操作会相互阻塞,而且其相对应的进程也会受到影响。这和其他的永久数据存储解决方案是一样的,所以我们建议你使用常用的,也是最有效的方案, 将所有写入放到一个单独的进程中。还要注意的是,因为realm的MVCC结构, 读取并不会 因为一个进行中的写事务而受到影响。详细内容,请阅读RLMObject。

当使用 RLMObject 属性的时候,您可以通过正常的属性访问语法来访问嵌套属性。比如说,rex.owner?.address.country 会依次读取对象的属性,然后自动从 Relam 中匹配所需的每一个对象。

通过在写入事务中将要删除的对象传递给-[RLMRealm deleteObject:]方法,即可完成删除操作。

3.关系

 

对一关系
     当一个对象持有另外一个对象时, 比如人有一个宠物

TAG标签:
版权声明:本文由必威发布于必威-编程,转载请注明出处:Realm数据模型是基于标准 Objective‑C,令人惊叹