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
。你也可以这样编写这个函数:
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
实例作为参数是非常痛苦的。RealmIO
RealmIO<RW, T>
表示一个 Realm 操作。
RW
实际上是 ReadOnly
或 ReadWrite
。它表示该操作是只读的还是读写的。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(这表示 io
是 RealmRW<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.add
、Realm.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版本中完成。我正在考虑下一个版本后的措施。
RealmIO
支持以下平台。
RealmIO
受到了来自Slick的DBIOAction
的启发。