Slate 是一个位于 Core Data 对象图之上的中间件,提供
使用你典型的 Core Data NSManagedObjects
class CoreDataBook: NSManagedObject {
@NSManaged public var id: UUID
@NSManaged public var pageCount: Int64
}
Slate 自动生成不可变表示
/* Auto-generated */
struct Book {
let id: UUID
let pageCount: Int
}
从只读上下文中查询,该上下文提供 Core Data 模型的这些不可变版本
func fetchBooksWithAtLeast(pageCount: Int, completion: ([Book]) -> Void) {
slate.queryAsync { readContext in
// Run queries on a Core Data object graph proxy that returns immutable objects.
// Slate handles the conversion behind the scenes.
let books = try readContext[Book.self].filter("pageCount > \(pageCount)").fetch()
// You can now pass `books` around wherever you want in a thread-safe manner.
// They are fully immutable and thread-safe.
completion(books)
}.catch { error in
// The optional trailing catch method allows you to batch all try-based calls inside
// of the transaction (similar to PromiseKit)
print(error)
}
}
继续使用 NSManagedObjectContext 进行写入,但在安全的单写/多读队列中操作
func updateBookPageCount(id: UUID, newPageCount: Int) {
slate.mutateAsync { moc in
// Mutate NSManagedObjects in a single-writer MOC. Insert/delete/updates are
// announced to all registered listeners on completion of the mutation block.
if let book = try moc[CoreDataBook.self].filter("id = %@", id).fetchOne() {
book.pageCount = newPageCount
}
// An optional Any? return value is passed along to transaction listeners
// to help indicate the context of the transaction.
return ExampleEnum.updatePageCount(id: id)
}
}
监听事务
class SomeClass: SlateMutationListener {
func slateMutationHandler(result: SlateMutationResult) {
// Handle Slate Mutation
}
}
...
let myClass = SomeClass()
slate.addListener(myClass)
为什么你想要为你的 Core Data 对象图使用不可变数据模型访问模式?
不可变模型无法改变。它们可以在后台线程上查询/创建,并在发送到主线程进行 UI 更新之前,用于任何复杂的排序/确定逻辑(因此主线程保持流畅)。不可变模型属性不必同步,可以直接访问。
不可变模型的作用类似于快照。如果你有多个功能使用相同的底层对象图,你的功能可以防止其他代码在没有严格知情的情况下改变其快照。 这扩展到关系——如果另一个功能删除了对象关系,功能的快照不会改变。相反,你会被通知更改,并可以在准备好时刷新/查询关系。
不可变模型有助于强制执行单向信息流。你不能编写就地“更新”不可变模型的方法。 相反,对象图的突变必须以强制对对象图进行事务更新的方式发生,然后依次向监听器宣布更改,监听器可以以受控方式重新获取其快照。
在 Core Data 之上使用不可变数据模型的缺点是什么?
Core Data 具有延迟加载托管对象(faulting)的能力。这与不可变数据模型的概念相互排斥。 在 Slate 中,所有查询的不可变对象都将被完全加载并存储在内存中。
这意味着如果你的应用程序不断查询/更新数万个托管对象,并且你需要 faulting 来保持效率,那么 Slate 将不是一个好的解决方案。
在 Core Data 中,你可以访问托管对象的关系来动态查询相关对象。 在 Slate 中,你必须预先获取这些关系作为不可变对象的数组,因为它们是快照的一部分。 关系不能在 Slate 查询上下文之外获取。
Slate 不是一个独立的 Cocoapod/Carthage 库。 它是一套代码,你可以选择如何将其集成到你的应用程序中。
在当前的 repo 中,这只包含 Slate 的 Swift + Core Data 实现。未来可能会支持其他语言和底层存储。
整个实现位于一个 swift 文件中。 除了编译速度更快之外,即使你将代码放在顶级应用程序中,这也允许 Slate 使用 fileprivate 来强制执行跨类保护。
你可以简单地将 Slate.swift
放入你的应用程序中,或者创建一个单独的框架并导入它。
这是一个单独的应用程序,用于生成 Core Data 模型的不可变版本。 它读取你的 xcdatamodel XML 文件并输出所需的类/结构。 有关详细信息和用法,请查看 SlateGenerator 目录中的 README。
这是一个简单的协议,可以帮助生成更新/删除/移动/重新加载索引,以将一个列表更新到另一个列表。 这主要用于 UITableView/UICollectionView 的 performBatchUpdates
方法,并且由于 NSFetchedResultsController
不能与 Slate 结合使用,因此提供了该协议。