RealmIO 2.1.0

RealmIO 2.1.0

测试已测试
语言语言 SwiftSwift
许可证 MIT
发布上次发布2018年2月
SPM支持 SPM

ukitaka 维护。



RealmIO 2.1.0

  • ukitaka




RealmIO 通过使用 reader monad 使 Realm 操作更安全、可重用和可组合。

动机

Realm 操作(尤其是 write 操作)如果你按照以下方式编写函数则不可重用:

func addDog(name: String) throws {
    let realm = try Realm()
    try realm.write {
        let dog = Dog()
        dog.name = name
        realm.add(dog)
    }
}

乍一看,这运行良好,但如果你多次调用这个函数,实际上存在一些问题。

addDog(name: "Taro")
addDog(name: "Jiro")
  • 你不能在同一个事务中添加两个狗对象。在这种情况下,会两次调用 realm.write
  • 通常,开始事务非常慢,realm.write 会锁定 realm 实例。我们不应当不需要地调用 realm.write

img img

你也可以这样编写这个函数:

func addDog(name: String, to realm: Realm) {
    let dog = Dog()
    dog.name = name
    realm.add(dog)
}
try realm.write {
    addDog(name: "Taro", to: realm)
    addDog(name: "Jiro", to: realm)
}

2 次调用 addDog 将在同一个事务中运行,但用户需要自己调用 realm.write

  • 用户不能从签名中判断是否需要自己开始一个事务。
  • 有时明确地传递 Realm 实例作为参数是非常痛苦的。

用法

把 Realm 操作定义为 RealmIO

RealmIO<RW, T> 表示一个 Realm 操作。

  • RW 实际上是 ReadOnlyReadWrite。它表示该操作是只读的还是读写的。
  • T 是返回值类型。

你还可以使用 RealmRO<T>RealmRW<T>,这些都是 RealmIO<ReadOnly, T>RealmIO<ReadWrite, T> 的别名。

public typealias RealmRO<T> = RealmIO<ReadOnly, T>

public typealias RealmRW<T> = RealmIO<ReadWrite, T>

例如,从 realm 中读取 User 对象的操作类型为 RealmRO<User>

func find(by userID: Int) -> RealmRO<User> {
    ...
}

如果你已经了解关于 reader monad 的知识,那么 RealmIO<RW, T>Reader<Realm, T> 类似,只是类型参数 RW 不同。

使用 realm.run(io:) 运行 Realm 操作

你可以使用 realm.run(io:) 运行前面的 realm 操作。

let io: RealmRO<User> = find(by: 123)
let result = try? realm.run(io: io)

如果操作需要写入 realm(这表示 ioRealmRW<T> 的实例),
realm.run(io:) 会自动开始事务。

realm.run(io:) 抛出两种错误类型。

  • Realm.Error
  • 由用户抛出的错误

使用 flatMap 组合 realm 操作

flatMap 允许你组合 realm 动作。

func add(dog: Dog) -> RealmRW<Void> {
    ...
}

func add(cat: Cat) -> RealmRW<Void> {
    ...
}

let io: RealmRW<Void> = add(dog: myDog).flatMap { _ in add(cat: myCat) }

并且你可以在一个同一个事务中运行组合操作。

realm.run(io: io) // Add `myDog` and `myCat` in a same transaction.

组合操作的 RW 类型参数由两种操作类型决定。

read.flatMap { _ in read }   // ReadOnly
read.flatMap { _ in write }  // ReadWrite
write.flatMap { _ in read }  // ReadWrite
write.flatMap { _ in write } // ReadWrite

使用方便的运算符

Realm.IO 提供了一些有用的运算符来创建 RealmIO 实例。
见:Realm+Operator.swift

注意:由 RealmIO 提供的某些方法还不是线程安全的。

一些将Object作为参数的方法,如Realm.IO.addRealm.IO.delete,目前不是线程安全的。
直接传递Object并不是更好的做法。如果你想安全地使用这个方法,你应该在同一个线程中调用realm.run(io:),或者使用flatMap

// OK: call `realm.run(io:)` in a same thread.
let io1 = Realm.IO.add(object)
try realm.run(io: io1)

// OK: use with `flatMap`
let io2 = Realm.IO.objects(Dog.self).flatMap(Realm.IO.delete)
try realm.run(io: io2)

由于ThreadSafeReference有一个约束,即不能在写事务中对引用进行解引用,因此使用ThreadSafeReference的实现不能在2.0版本中完成。我正在考虑下一个版本后的措施。

需求

  • Xcode 9
  • swift 4.0
  • Realm ~> 3.0

RealmIO支持以下平台。

  • iOS 8.0+
  • macOS 10.10+
  • watchOS 2.0+
  • tvOS 9.0+

致谢

RealmIO受到了来自SlickDBIOAction的启发。