如何从特定对象恐怖症案例ID获取Core Data对象

CoreStore 做得更好
每个数据栈支持多个 persistent store。如 .xcdatamodeld 文件设计的那样,CoreStore还会将一个数据栈作为默认栈,但是你可以创建和管理多个栈。
渐进式迁移 :只需要告诉数据栈模型版本的顺序,CoreStore 会自动使用渐进式迁移。
创建自己的日志框架。
其他Core Data的封装都有个局限,那就是实体名字一定要和相应的类名一致。CoreStore从管理对象模型文件中导入实体到类的映射,因此实体名字和相应的类名无需一致。
提供类型安全、配置简单的观察者模式,替代 NSFetchedResultsController 和 KVO 。
不只有 fetching 的API接口,还可以查询聚集值和属性类型的API。
为了减少常见并发性错误的发生,NSManagedObjectContext任务在封装成更安全,更高层次的抽象。与此同时还保留了NSManagedObjectContext任务灵活性和可定制性。
基于Swift语言的优雅以及类型安全性,CoreStore提供了干净方便的API。
提供文档。这里没有魔法,类、方法、特征都是公开的。这篇README中也介绍了大量的概念以及解释了大量CoreStore的原理。
高效导入机制。
初始化设置渐进式迁移
CoreStore.defaultStack = DataStack(
modelName: "MyStore",
migrationChain: ["MyStore", "MyStoreV2", "MyStoreV3"]
CoreStore.defaultStack = DataStack(&&&&modelName: "MyStore",&&&&migrationChain: ["MyStore", "MyStoreV2", "MyStoreV3"])
try CoreStore.addSQLiteStore(
fileName: "MyStore.sqlite",
completion: { (result) -& Void in
try CoreStore.addSQLiteStore(&&&&fileName: "MyStore.sqlite",&&&&completion: { (result) -& Void in&&&&&&&&// ...&&&&})
开始进行数据处理
CoreStore.beginAsynchronous { (transaction) -& Void in
let person = transaction.create(Into(MyPersonEntity))
person.name = "John Smith"
person.age = 42
transaction.commit { (result) -& Void in
switch result {
case .Success(let hasChanges): print("success!")
case .Failure(let error): print(error)
123456789101112
CoreStore.beginAsynchronous { (transaction) -& Void in&&&&let person = transaction.create(Into(MyPersonEntity))&&&&person.name = "John Smith"&&&&person.age = 42&&&&&transaction.commit { (result) -& Void in&&&&&&&&switch result {&&&&&&&&&&&&case .Success(let hasChanges): print("success!")&&&&&&&&&&&&case .Failure(let error): print(error)&&&&&&&&}&&&&}}
let people = CoreStore.fetchAll(From(MyPersonEntity))
let people = CoreStore.fetchAll(From(MyPersonEntity))
let people = CoreStore.fetchAll(
From(MyPersonEntity),
Where("age & 30"),
OrderBy(.Ascending("name"), .Descending("age")),
Tweak { (fetchRequest) -& Void in
fetchRequest.includesPendingChanges = false
let people = CoreStore.fetchAll(&&&&From(MyPersonEntity),&&&&Where("age & 30"),&&&&OrderBy(.Ascending("name"), .Descending("age")),&&&&Tweak { (fetchRequest) -& Void in&&&&&&&&fetchRequest.includesPendingChanges = false&&&&})
let maxAge = CoreStore.queryValue(
From(MyPersonEntity),
Select&Int&(.Maximum("age"))
let maxAge = CoreStore.queryValue(&&&&From(MyPersonEntity),&&&&Select&Int&(.Maximum("age")))
好了我写这儿长的README是有原因的,接下来我们来看看其中的具体实现。
为了最大程度的安全和性能表现,CoreStore将强制编码模式(别担心,没有听起来那么可怕)。
最好在使用CoreStore之前先了解一些运作原理。
如果你已经非常熟悉 CoreData 的内部工作原理,这里是一张CoreStore的概念对照表。
一些有名的开发库比如 RestKit 和 MagicalRecord 是这样布置他们的NSManagedObjectContexts:
嵌套的上下文存储路径从 context 子类到 context根类,最大程度地确保上下文间数据完整性,同时不阻塞主线程。
但是根据 ,合并上下文远比嵌套上下文的存储速度快。
CoreStore 的 DataStack 集两种方式的长处,将主要 NSManagedObjectContext 作为一个只读 context,只允许子类 NSManagedObjectContext进行数据处理。
这样我们既能拥有一个黄油般顺滑的主线程,以及安全的嵌套上下文的优势。
初始化 CoreStore 最简单的方法就是在默认的栈中加入默认的仓库。
try CoreStore.addSQLiteStoreAndWait()
do {&&&&try CoreStore.addSQLiteStoreAndWait()}catch {&&&&// ...}
这行代码做了以下几件事情:
使用一个默认 DataStack 来对 CoreStore.defaultStack 进行懒加载。
设置该栈的 NSPersistentStoreCoordinator,底层存储 NSManagedObjectContext,以及一个只读主要 NSManagedObjectContext。
在Application Support(或者是tvOS的Caches文件中)文件中添加一个SQLite仓库,文件名是 “[App bundle name].sqlite”。
创建并且成功返回 NSPersistentStore 的实例,或者返回失败的NSError。
大多数情况下,这些配置就已经足够使用了。但是对需要更加多的设置,请参阅以下例子:
let dataStack = DataStack(
modelName: "MyModel", // loads from the "MyModel.xcdatamodeld" file
migrationChain: ["MyStore", "MyStoreV2", "MyStoreV3"] // model versions for progressive migrations
// creates an in-memory store with entities from the "Config1" configuration in the .xcdatamodeld file
let persistentStore = try dataStack.addInMemoryStoreAndWait(configuration: "Config1") // persistentStore is an NSPersistentStore instance
print("Successfully created an in-memory store: \(persistentStore)"
print("Failed creating an in-memory store with error: \(error as NSError)"
try dataStack.addSQLiteStore(
fileURL: sqliteFileURL, // set the target file URL for the sqlite file
configuration: "Config2", // use entities from the "Config2" configuration in the .xcdatamodeld file
resetStoreOnModelMismatch: true,
completion: { (result) -& Void in
switch result {
case .Success(let persistentStore):
print("Successfully added sqlite store: \(persistentStore)"
case .Failure(let error):
print("Failed adding sqlite store with error: \(error)"
print("Failed adding sqlite store with error: \(error as NSError)"
CoreStore.defaultStack = dataStack // pass the dataStack to CoreStore for easier access later on
12345678910111213141516171819202122232425262728293031323334
let dataStack = DataStack(&&&&modelName: "MyModel", // loads from the "MyModel.xcdatamodeld" file&&&&migrationChain: ["MyStore", "MyStoreV2", "MyStoreV3"] // model versions for progressive migrations)&do {&&&&// creates an in-memory store with entities from the "Config1" configuration in the .xcdatamodeld file&&&&let persistentStore = try dataStack.addInMemoryStoreAndWait(configuration: "Config1") // persistentStore is an NSPersistentStore instance&&&&print("Successfully created an in-memory store: \(persistentStore)"}catch {&&&&print("Failed creating an in-memory store with error: \(error as NSError)"}&do {&&&&try dataStack.addSQLiteStore(&&&&&&&&fileURL: sqliteFileURL, // set the target file URL for the sqlite file&&&&&&&&configuration: "Config2", // use entities from the "Config2" configuration in the .xcdatamodeld file&&&&&&&&resetStoreOnModelMismatch: true,&&&&&&&&completion: { (result) -& Void in&&&&&&&&&&&&switch result {&&&&&&&&&&&&case .Success(let persistentStore):&&&&&&&&&&&&&&&&print("Successfully added sqlite store: \(persistentStore)"&&&&&&&&&&&&case .Failure(let error):&&&&&&&&&&&&&&&&print("Failed adding sqlite store with error: \(error)"&&&&&&&&&&&&}&&&&&&&&}&&&&)}catch {&&&&print("Failed adding sqlite store with error: \(error as NSError)"}&CoreStore.defaultStack = dataStack // pass the dataStack to CoreStore for easier access later on
(如果你从来没有听过“Configurations”, 你会在 .xcdatamodeld 文件中找到)
上面的示例代码中,可以不需要写 CoreStore.defaultStack = dataStack。而是像下面这样声明一个 DataStack 实例,然后直接调用它的实例方法:
class MyViewController: UIViewController {
let dataStack = DataStack(modelName: "MyModel")
override func viewDidLoad() {
super.viewDidLoad()
try self.dataStack.addSQLiteStoreAndWait()
catch { // ...
func methodToBeCalledLaterOn() {
let objects = self.dataStack.fetchAll(From(MyEntity))
print(objects)
123456789101112131415
class MyViewController: UIViewController {&&&&let dataStack = DataStack(modelName: "MyModel")&&&&override func viewDidLoad() {&&&&&&&&super.viewDidLoad()&&&&&&&&do {&&&&&&&&&&&&try self.dataStack.addSQLiteStoreAndWait()&&&&&&&&}&&&&&&&&catch { // ...&&&&&&&&}&&&&}&&&&func methodToBeCalledLaterOn() {&&&&&&&&let objects = self.dataStack.fetchAll(From(MyEntity))&&&&&&&&print(objects)&&&&}}
不同点在于,当你把这个 stack 设置为 CoreStore.defaultStack,你就可以直接从 CoreStore 调用该 stack 的方法。
class MyViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
try CoreStore.addSQLiteStoreAndWait()
catch { // ...
func methodToBeCalledLaterOn() {
let objects = CoreStore.fetchAll(From(MyEntity))
print(objects)
1234567891011121314
class MyViewController: UIViewController {&&&&override func viewDidLoad() {&&&&&&&&super.viewDidLoad()&&&&&&&&do {&&&&&&&&&&&&try CoreStore.addSQLiteStoreAndWait()&&&&&&&&}&&&&&&&&catch { // ...&&&&&&&&}&&&&}&&&&func methodToBeCalledLaterOn() {&&&&&&&&let objects = CoreStore.fetchAll(From(MyEntity))&&&&&&&&print(objects)&&&&}}
到现在为止,我们只是使用了 addSQLiteStoreAndWait(…) 来初始化我们的持久化仓库。正如该方法名字的后缀 “AndWait” 提示的那样,该方法会引起阻塞,所以不能用来做一些长时间的任务,比如存储迁移(实际上CoreStore根本不会尝试去做,如果发生了 model 不匹配会被当做错误进行报告),如果需要进行数据迁移应当使用该方法的异步版本 addSQLiteStore(… completion:)。
let progress: NSProgress? = try dataStack.addSQLiteStore(
fileName: "MyStore.sqlite",
configuration: "Config2",
completion: { (result) -& Void in
switch result {
case .Success(let persistentStore):
print("Successfully added sqlite store: \(persistentStore)")
case .Failure(let error):
print("Failed adding sqlite store with error: \(error)")
print("Failed adding sqlite store with error: \(error as NSError)"
1234567891011121314151617
do {&&&&let progress: NSProgress? = try dataStack.addSQLiteStore(&&&&&&&&fileName: "MyStore.sqlite",&&&&&&&&configuration: "Config2",&&&&&&&&completion: { (result) -& Void in&&&&&&&&&&&&switch result {&&&&&&&&&&&&case .Success(let persistentStore):&&&&&&&&&&&&&&&&print("Successfully added sqlite store: \(persistentStore)")&&&&&&&&&&&&case .Failure(let error):&&&&&&&&&&&&&&&&print("Failed adding sqlite store with error: \(error)")&&&&&&&&&&&&}&&&&&&&&}&&&&)}catch {&&&&print("Failed adding sqlite store with error: \(error as NSError)"}
在 completion 块中返回一个 PersistentStoreResult 值来表明创建是成功还是失败。
存储指定的URL与已经存在的仓库存在冲突,一个存在的sqlite文件不能读取都会引起 addSQLiteStore(…) 报错。如果报错 completion 块不会运行,该方法同样会返回一个可选的 NSProgress。如果是 nil,则不需要迁移。如果返回nil,你可以使用KVO的 “fractionCompleted” 值来跟踪迁移过程,或者通过 NSProgress+Convenience.swift 中暴露的闭包。
progress?.setProgressHandler { [weak self] (progress) -& Void in
self?.progressView?.setProgress(Float(progress.fractionCompleted), animated: true)
self?.percentLabel?.text = progress.localizedDescription // "50% completed"
self?.stepLabel?.text = progress.localizedAdditionalDescription // "0 of 2"
progress?.setProgressHandler { [weak self] (progress) -& Void in&&&&self?.progressView?.setProgress(Float(progress.fractionCompleted), animated: true)&&&&self?.percentLabel?.text = progress.localizedDescription // "50% completed"&&&&self?.stepLabel?.text = progress.localizedAdditionalDescription // "0 of 2"}
这个闭包在主线程执行。
渐进式迁移
一般来说,CoreStore 使用的是 Core Data 默认的自动迁移机制,也就是说 CoreStore 会尝试将先手的 persistent store 迁移到 .xcdatamodeld 文件的当前版本。如果没有 store 版本到 model 版本的映射,CoreStore就会放弃并且报错。
DataStack 可以通过 MingrationChain 实现迁移,这点在 DataStack 实例初始化的时候进行设置,并且用于 DataStack 通过 addSQLiteStore(…) 及其变体添加的所有仓库。
let dataStack = DataStack(migrationChain:
["MyAppModel", "MyAppModelV2", "MyAppModelV3", "MyAppModelV4"])
let dataStack = DataStack(migrationChain: &&&&["MyAppModel", "MyAppModelV2", "MyAppModelV3", "MyAppModelV4"])
最常用的方法如上按顺序传入 .xcdatamodeld 版本名。
更加复杂的迁移路径,你可以用源/目标的键值对形式传入版本树。
let dataStack = DataStack(migrationChain: [
"MyAppModel": "MyAppModelV3",
"MyAppModelV2": "MyAppModelV4",
"MyAppModelV3": "MyAppModelV4"
let dataStack = DataStack(migrationChain: [&&&&"MyAppModel": "MyAppModelV3",&&&&"MyAppModelV2": "MyAppModelV4",&&&&"MyAppModelV3": "MyAppModelV4"])
上面的案例对应的版本路径。
MyAppModel-MyAppModelV3-MyAppModelV4
MyAppModelV2-MyAppModelV4
MyAppModelV3-MyAppModelV4
初始化的时候使用空值表示不是用渐进式迁移而是使用默认迁移方式(当前.xcdatamodel版本作为最后版本)。
let dataStack = DataStack(migrationChain: nil)
let dataStack = DataStack(migrationChain: nil)
除非是空值,当 MigrationChain 传值给 DataStack 后就生效了,但是下面情况发生时会触发断言:
一个版本数组中出现两次
一个版本在字典中两次作为key出现
路径中出现循环
记住如果指定了MigrationChain、.xcdataModeld的当前版本就会被设为旁路,MigrationChain的枝末版本将作为DataStack的基本模型版本。
预测式迁移
有时候迁移量很大,app需要做个 loading 界面或者其他形式和用户进行交互。CoreStore提供了 requiredMigrationsForSQLiteStore(…) 方法用来在调用 addSQLiteStore(…) 前检查persistent store。
let migrationTypes: [MigrationType] = CoreStore.requiredMigrationsForSQLiteStore(fileName: "MyStore.sqlite")
if migrationTypes.count & 1
|| (migrationTypes.filter { $0.isHeavyweightMigration }.count) & 0 {
// ... Show special waiting screen
else if migrationTypes.count & 0 {
// ... Show simple activity indicator
// ... Do nothing
CoreStore.addSQLiteStore(/* ... */)
123456789101112131415161718
do {&&&&let migrationTypes: [MigrationType] = CoreStore.requiredMigrationsForSQLiteStore(fileName: "MyStore.sqlite")&&&&if migrationTypes.count & 1&&&&&&&&|| (migrationTypes.filter { $0.isHeavyweightMigration }.count) & 0 {&&&&&&&&// ... Show special waiting screen&&&&}&&&&else if migrationTypes.count & 0 {&&&&&&&&// ... Show simple activity indicator&&&&}&&&&else {&&&&&&&&// ... Do nothing&&&&}&&&&&CoreStore.addSQLiteStore(/* ... */)}catch {&&&&// ...}
requiredMigrationsForSQLiteStore(…) 返回MigrationType的数组,可能的值:
case Lightweight(sourceVersion: String, destinationVersion: String)
case Heavyweight(sourceVersion: String, destinationVersion: String)
case Lightweight(sourceVersion: String, destinationVersion: String)case Heavyweight(sourceVersion: String, destinationVersion: String)
每个 MigrationType 指明 MigrationChain 中每一步迁移的类型。
保存和处理数据
为了确保只读 NSManagedObjectContext 的对象不被随意改变,CoreStore并没有开放直接更新和保存主 context 和其他 context 的API。你只能通过 DataStack 实例进行数据处理。
let dataStack = self.dataStack
dataStack.beginAsynchronous { (transaction) -& Void in
// make changes
transaction.commit()
let dataStack = self.dataStackdataStack.beginAsynchronous { (transaction) -& Void in&&&&// make changes&&&&transaction.commit()}
或者直接调用CoreStore的默认stack。
CoreStore.beginAsynchronous { (transaction) -& Void in
// make changes
transaction.commit()
CoreStore.beginAsynchronous { (transaction) -& Void in&&&&// make changes&&&&transaction.commit()}
commit() 这个方法将数据变化保存到 persistent store。如果在数据处理块完成后没有调用 commit(),所有的变化都无效被丢弃。
上面的例子使用的是 beginAsynchronous(…),实际上我们提供了三种同步、异步、不安全事务类型。
相应的方法:beginAsynchronous(…)。该方法立即返回并在后台串行队列执行其闭包内容
CoreStore.beginAsynchronous { (transaction) -& Void in
// make changes
transaction.commit()
CoreStore.beginAsynchronous { (transaction) -& Void in&&&&// make changes&&&&transaction.commit()}
其中的 transaction 是 AsynchronousDataTransaction 类型。
相应的方法 beginSynchronous(…)。句法与异步处理类似,但是同步处理是在transaction块完成后才返回值。
CoreStore.beginSynchronous { (transaction) -& Void in
// make changes
transaction.commit()
CoreStore.beginSynchronous { (transaction) -& Void in&&&&// make changes&&&&transaction.commit()}
上面的 transaction 是 SynchronousDataTransaction 类型。
技术上来说 beginSynchronous(…) 会阻塞两条线程(回调线程和事务后台线程),这更不安全更容易形成死锁。注意这个闭包并不会阻塞其他线程。
不安全事务
不在闭包内进行数据更新:
let transaction = CoreStore.beginUnsafe()
// make changes
downloadJSONWithCompletion({ (json) -& Void in
// make other changes
transaction.commit()
downloadAnotherJSONWithCompletion({ (json) -& Void in
// make some other changes
transaction.commit()
123456789101112
let transaction = CoreStore.beginUnsafe()// make changesdownloadJSONWithCompletion({ (json) -& Void in&&&&&// make other changes&&&&transaction.commit()})downloadAnotherJSONWithCompletion({ (json) -& Void in&&&&&// make some other changes&&&&transaction.commit()})
该方法允许不连续的更新。但是同时你需要手动管理事件的并发性,这就是“能力越大责任越大”。
上面的例子我们还可以看出,只有不安全事务可以多次调用 commit(),在同步和异步事务中这么做会触发断言。
我们已经知道怎么创建事务,接下来我们要来介绍 BaseDataTransaction 的三种子类事务——创建、更新、删除。
create(…) 方法接收一个Into分句,Into分句指明了你想要创建的对象实体:
let person = transaction.create(Into(MyPersonEntity))
let person = transaction.create(Into(MyPersonEntity))
这个句法非常直接,CoreStore并不只是插入一个新对象,这句代码做了以下几件事
检查实体类型是否存在
如果该实体只属于一个persistent store,新的对象就被插入该仓库并且通过 create() 返回
如果不属于任何仓库,断言被触发。这是个不该发生的错误。
如果该实体属于多个仓库,断言也会被触发。这也是个不该发生的错误。一般在这种情况下,使用Core Data你可以插入一个对象但是保存Context的时候会发生错误。CoreStore则在创建的时候就帮你判断这一项。
如果该实体存在在多个persistent store中,你需要提供 store 的名字:
let person = transaction.create(Into&MyPersonEntity&("Config1"))
let person = transaction.create(Into&MyPersonEntity&("Config1"))
如果使用 nil 这表示是默认的persistent store:
let person = transaction.create(Into&MyPersonEntity&(nil))
let person = transaction.create(Into&MyPersonEntity&(nil))
注意如果指明了配置名,CoreStore只会尝试在该 store 中插入新创建的对象,如果该 store 没有找到返回失败,并不会倒回查找该实例应该从属的 store。
创建完对象后就可以更新了。
CoreStore.beginAsynchronous { (transaction) -& Void in
let person = transaction.create(Into(MyPersonEntity))
person.name = "John Smith"
person.age = 30
transaction.commit()
CoreStore.beginAsynchronous { (transaction) -& Void in&&&&let person = transaction.create(Into(MyPersonEntity))&&&&person.name = "John Smith"&&&&person.age = 30&&&&transaction.commit()}
要更新已经存在的对象,首先要从事务中获取对象的实例:
CoreStore.beginAsynchronous { (transaction) -& Void in
let person = transaction.fetchOne(
From(MyPersonEntity),
Where("name", isEqualTo: "Jane Smith")
person.age = person.age + 1
transaction.commit()
CoreStore.beginAsynchronous { (transaction) -& Void in&&&&let person = transaction.fetchOne(&&&&&&&&From(MyPersonEntity),&&&&&&&&Where("name", isEqualTo: "Jane Smith")&&&&)&&&&person.age = person.age + 1&&&&transaction.commit()}
不要更新不存在或者不是从事务中获取出来的实例。获取到该对象都就可以通过 transaction 的 edit(…) 方法获取其可编辑的实例。
let jane: MyPersonEntity = // ...
CoreStore.beginAsynchronous { (transaction) -& Void in
// WRONG: jane.age = jane.age + 1
let jane = transaction.edit(jane)! // using the same variable name protects us from misusing the non-transaction instance
jane.age = jane.age + 1
transaction.commit()
let jane: MyPersonEntity = // ...&CoreStore.beginAsynchronous { (transaction) -& Void in&&&&// WRONG: jane.age = jane.age + 1&&&&// RIGHT:&&&&let jane = transaction.edit(jane)! // using the same variable name protects us from misusing the non-transaction instance&&&&jane.age = jane.age + 1&&&&transaction.commit()}
还可以用来更新对象之间的关系。也要确保对象所属的关系是事务创造或者从中获取:
let jane: MyPersonEntity = // ...
let john: MyPersonEntity = // ...
CoreStore.beginAsynchronous { (transaction) -& Void in
// WRONG: jane.friends = [john]
let jane = transaction.edit(jane)!
let john = transaction.edit(john)!
jane.friends = NSSet(array: [john])
transaction.commit()
1234567891011
let jane: MyPersonEntity = // ...let john: MyPersonEntity = // ...&CoreStore.beginAsynchronous { (transaction) -& Void in&&&&// WRONG: jane.friends = [john]&&&&// RIGHT:&&&&let jane = transaction.edit(jane)!&&&&let john = transaction.edit(john)!&&&&jane.friends = NSSet(array: [john])&&&&transaction.commit()}
删除对象简单很多,你只需要直接调用 transaction 的 delete 方法而不必获取可编辑版本:
let john: MyPersonEntity = // ...
CoreStore.beginAsynchronous { (transaction) -& Void in
transaction.delete(john)
transaction.commit()
let john: MyPersonEntity = // ...&CoreStore.beginAsynchronous { (transaction) -& Void in&&&&transaction.delete(john)&&&&transaction.commit()}
还可以一次删除多个对象:
let john: MyPersonEntity = // ...
let jane: MyPersonEntity = // ...
CoreStore.beginAsynchronous { (transaction) -& Void in
transaction.delete(john, jane)
// transaction.delete([john, jane]) is also allowed
transaction.commit()
let john: MyPersonEntity = // ...let jane: MyPersonEntity = // ...&CoreStore.beginAsynchronous { (transaction) -& Void in&&&&transaction.delete(john, jane)&&&&// transaction.delete([john, jane]) is also allowed&&&&transaction.commit()}
transaction还提供了deleteAll(…)方法来设置查询条件确定需要删除的对象
CoreStore.beginAsynchronous { (transaction) -& Void in
transaction.deleteAll(
From(MyPersonEntity)
Where("age & 30")
transaction.commit()
CoreStore.beginAsynchronous { (transaction) -& Void in&&&&transaction.deleteAll(&&&&&&&&From(MyPersonEntity)&&&&&&&&Where("age & 30")&&&&)&&&&transaction.commit()}
安全地传递对象
记住 DataStack 和单独的事务管理和 NSManageObjectContext 是不同的,所以 transaction 有一个 edit() 方法:
let jane: MyPersonEntity = // ...
CoreStore.beginAsynchronous { (transaction) -& Void in
let jane = transaction.edit(jane)!
jane.age = jane.age + 1
transaction.commit()
let jane: MyPersonEntity = // ...&CoreStore.beginAsynchronous { (transaction) -& Void in&&&&let jane = transaction.edit(jane)!&&&&jane.age = jane.age + 1&&&&transaction.commit()}
但是CoreStore,DataStack和BaseDataTransaction有个一非常灵活的fetchExisting方法,可以用来来回传值
let jane: MyPersonEntity = // ...
CoreStore.beginAsynchronous { (transaction) -& Void in
let jane = transaction.fetchExisting(jane)! // instance for transaction
jane.age = jane.age + 1
transaction.commit {
let jane = CoreStore.fetchExisting(jane)! // instance for DataStack
print(jane.age)
12345678910
let jane: MyPersonEntity = // ...&CoreStore.beginAsynchronous { (transaction) -& Void in&&&&let jane = transaction.fetchExisting(jane)! // instance for transaction&&&&jane.age = jane.age + 1&&&&transaction.commit {&&&&&&&&let jane = CoreStore.fetchExisting(jane)! // instance for DataStack&&&&&&&&print(jane.age)&&&&}}
fetchExisting(…) 还可以用于多个 NAManagedObject 或者 NSManagedObjectID:
var peopleIDs: [NSManagedObjectID] = // ...
CoreStore.beginAsynchronous { (transaction) -& Void in
let jane = transaction.fetchOne(
From(MyPersonEntity),
Where("name", isEqualTo: "Jane Smith")
jane.friends = NSSet(array: transaction.fetchExisting(peopleIDs)!)
12345678910
var peopleIDs: [NSManagedObjectID] = // ...&CoreStore.beginAsynchronous { (transaction) -& Void in&&&&let jane = transaction.fetchOne(&&&&&&&&From(MyPersonEntity),&&&&&&&&Where("name", isEqualTo: "Jane Smith")&&&&)&&&&jane.friends = NSSet(array: transaction.fetchExisting(peopleIDs)!)&&&&// ...}
有时候我们存入Core Data的值是来自外部文件,例如网络服务器或者其他文件。所以如果你有一个JSON格式的字典,你可能会这样取值:
let json: [String: AnyObject] = // ...
person.name = json["name"] as? NSString
person.age = json["age"] as? NSNumber
let json: [String: AnyObject] = // ...person.name = json["name"] as? NSStringperson.age = json["age"] as? NSNumber// ...
但是如果属性值很多,这一项重复工作量很大。使用 CoreStore 你只需要写一次,只要调用 importObject(…) 或者 importUniqueObject(…):
CoreStore.beginAsynchronous { (transaction) -& Void in
let json: [String: AnyObject] = // ...
try! transaction.importObject(
Into(MyPersonEntity),
source: json
transaction.commit()
CoreStore.beginAsynchronous { (transaction) -& Void in&&&&let json: [String: AnyObject] = // ...&&&&try! transaction.importObject(&&&&&&&&Into(MyPersonEntity),&&&&&&&&source: json&&&&)&&&&transaction.commit()}
为了导入数据,你需要在 NSManagedObject 的子类中实现 ImportableObject 或者 ImportableUniqueObject 协议:
ImportableObject:如果对象之间没什么不同点,新的对象通过调用 importObject(…) 进行添加
ImportableUniqueObject:该协议在创建或者更新的时候为对象分配一个唯一ID,调用 importUniqueObject(…) 方法
这两种协议都要求制定一个ImportSource,可以使任何类型用于对象导入数据:
typealias ImportSource = NSDictionary
typealias ImportSource = [String: AnyObject]
typealias ImportSource = NSData
typealias ImportSource = NSDictionarytypealias ImportSource = [String: AnyObject]typealias ImportSource = NSData
你可以从第三方JSON库或者直接从元组导入数据。
ImportableObject
ImportableObject是一个非常简单的协议。
public protocol ImportableObject: class {
typealias ImportSource
static func shouldInsertFromImportSource(source: ImportSource, inTransaction transaction: BaseDataTransaction) -& Bool
func didInsertFromImportSource(source: ImportSource, inTransaction transaction: BaseDataTransaction) throws
public protocol ImportableObject: class {&&&&typealias ImportSource&&&&static func shouldInsertFromImportSource(source: ImportSource, inTransaction transaction: BaseDataTransaction) -& Bool&&&&func didInsertFromImportSource(source: ImportSource, inTransaction transaction: BaseDataTransaction) throws}
首先将ImportSource设置成相应的数据类型,
typealias ImportSource = [String: AnyObject]
typealias ImportSource = [String: AnyObject]
这里将调用 importObject(_:source:)。其中,source 将使用 [String: AnyObject] 类型:
CoreStore.beginAsynchronous { (transaction) -& Void in
let json: [String: AnyObject] = // ...
try! transaction.importObject(
Into(MyPersonEntity),
source: json
CoreStore.beginAsynchronous { (transaction) -& Void in&&&&let json: [String: AnyObject] = // ...&&&&try! transaction.importObject(&&&&&&&&Into(MyPersonEntity),&&&&&&&&source: json&&&&)&&&&// ...}
具体的数据提取和分配在 ImportableObject 协议的 didInsertFromImportSource(…) 方法中实现:
func didInsertFromImportSource(source: ImportSource, inTransaction transaction: BaseDataTransaction) throws {
self.name = source["name"] as? NSString
self.age = source["age"] as? NSNumber
func didInsertFromImportSource(source: ImportSource, inTransaction transaction: BaseDataTransaction) throws {&&&&self.name = source["name"] as? NSString&&&&self.age = source["age"] as? NSNumber&&&&// ...}
你还可以使用importObjects(_:sourceArray:)一次导入多个对象
CoreStore.beginAsynchronous { (transaction) -& Void in
let jsonArray: [[String: AnyObject]] = // ...
try! transaction.importObjects(
Into(MyPersonEntity),
sourceArray: jsonArray
CoreStore.beginAsynchronous { (transaction) -& Void in&&&&let jsonArray: [[String: AnyObject]] = // ...&&&&try! transaction.importObjects(&&&&&&&&Into(MyPersonEntity),&&&&&&&&sourceArray: jsonArray&&&&)&&&&// ...}
这样迭代导入数组中的资源,然后再调用 shouldInsertFromImportSource(…) 来确定该创建哪个实例。你还可以进行验证,并通过shouldInsertFromImportSource(…) 返回 false 来跳过数组中其中一个数据的导入。
如果一个资源导入出现错误,所有的都会被取消。但是现在你可以在 didInsertFromImportSource(…) 中抛出一个错误。
func didInsertFromImportSource(source: ImportSource, inTransaction transaction: BaseDataTransaction) throws {
self.name = source["name"] as? NSString
self.age = source["age"] as? NSNumber
if self.name == nil {
throw Errors.InvalidNameError
func didInsertFromImportSource(source: ImportSource, inTransaction transaction: BaseDataTransaction) throws {&&&&self.name = source["name"] as? NSString&&&&self.age = source["age"] as? NSNumber&&&&// ...&&&&if self.name == nil {&&&&&&&&throw Errors.InvalidNameError&&&&}}
这么做你可以直接放弃一个无效事务。
CoreStore.beginAsynchronous { (transaction) -& Void in
let jsonArray: [[String: AnyObject]] = // ...
try transaction.importObjects(
Into(MyPersonEntity),
sourceArray: jsonArray
return // Woops, don't save
transaction.commit {
123456789101112131415
CoreStore.beginAsynchronous { (transaction) -& Void in&&&&let jsonArray: [[String: AnyObject]] = // ...&&&&do {&&&&&&&&try transaction.importObjects(&&&&&&&&&&&&Into(MyPersonEntity),&&&&&&&&&&&&sourceArray: jsonArray&&&&&&&&)&&&&}&&&&catch {&&&&&&&&return // Woops, don't save&&&&}&&&&transaction.commit {&&&&&&&&// ...&&&&}}
ImportableUniqueObject
一般我们不止导入数据还会进行更新数据。实现 Importable 协议可以让你给导入的每个数据添加一个唯一ID 用于搜索。
public protocol ImportableUniqueObject: ImportableObject {
typealias ImportSource
typealias UniqueIDType: NSObject
static var uniqueIDKeyPath: String { get }
var uniqueIDValue: UniqueIDType { get set }
static func shouldInsertFromImportSource(source: ImportSource, inTransaction transaction: BaseDataTransaction) -& Bool
static func shouldUpdateFromImportSource(source: ImportSource, inTransaction transaction: BaseDataTransaction) -& Bool
static func uniqueIDFromImportSource(source: ImportSource, inTransaction transaction: BaseDataTransaction) throws -& UniqueIDType?
func didInsertFromImportSource(source: ImportSource, inTransaction transaction: BaseDataTransaction) throws
func updateFromImportSource(source: ImportSource, inTransaction transaction: BaseDataTransaction) throws
12345678910111213
public protocol ImportableUniqueObject: ImportableObject {&&&&typealias ImportSource&&&&typealias UniqueIDType: NSObject&&&&&static var uniqueIDKeyPath: String { get }&&&&var uniqueIDValue: UniqueIDType { get set }&&&&&static func shouldInsertFromImportSource(source: ImportSource, inTransaction transaction: BaseDataTransaction) -& Bool&&&&static func shouldUpdateFromImportSource(source: ImportSource, inTransaction transaction: BaseDataTransaction) -& Bool&&&&static func uniqueIDFromImportSource(source: ImportSource, inTransaction transaction: BaseDataTransaction) throws -& UniqueIDType?&&&&func didInsertFromImportSource(source: ImportSource, inTransaction transaction: BaseDataTransaction) throws&&&&func updateFromImportSource(source: ImportSource, inTransaction transaction: BaseDataTransaction) throws}
注意其插入方法与 ImportableObject 相同,另外添加了更新和设置唯一ID 的方法。
class var uniqueIDKeyPath: String {
return "personID"
var uniqueIDValue: NSNumber {
get { return self.personID }
set { self.personID = newValue }
class func uniqueIDFromImportSource(source: ImportSource, inTransaction transaction: BaseDataTransaction) throws -& NSNumber? {
return source["id"] as? NSNumber
12345678910
class var uniqueIDKeyPath: String {&&&&return "personID" }var uniqueIDValue: NSNumber { &&&&get { return self.personID }&&&&set { self.personID = newValue }}class func uniqueIDFromImportSource(source: ImportSource, inTransaction transaction: BaseDataTransaction) throws -& NSNumber? {&&&&return source["id"] as? NSNumber}
在 ImportableUniqueObject 中数据的提取和分配在 updateFromImportSource(…) 中实现,默认情况下 didInsertFromImportSource(…) 调用updateFromImportSource(…),但是你也可以分开插入和更新过程。
然后,你可以调用 transaction 的 importUniqueObject(…)
方法实现创建和更新对象。
CoreStore.beginAsynchronous { (transaction) -& Void in
let json: [String: AnyObject] = // ...
try! transaction.importUniqueObject(
Into(MyPersonEntity),
source: json
CoreStore.beginAsynchronous { (transaction) -& Void in&&&&let json: [String: AnyObject] = // ...&&&&try! transaction.importUniqueObject(&&&&&&&&Into(MyPersonEntity),&&&&&&&&source: json&&&&)&&&&// ...}
或者调用 importUniqueObjects(…) 一次创建或更新多个对象。
CoreStore.beginAsynchronous { (transaction) -& Void in
let jsonArray: [[String: AnyObject]] = // ...
try! transaction.importUniqueObjects(
Into(MyPersonEntity),
sourceArray: jsonArray
CoreStore.beginAsynchronous { (transaction) -& Void in&&&&let jsonArray: [[String: AnyObject]] = // ...&&&&try! transaction.importUniqueObjects(&&&&&&&&Into(MyPersonEntity),&&&&&&&&sourceArray: jsonArray&&&&)&&&&// ...}
在 ImportableObject 中,你可以通过实现 shouldInsertFromImportSource(…) 和 shouldUpdateFromImportSource(…) 选择跳过导入一个对象。或者通过向 uniqueIDFromImportSource(…)、didInsertFromImportSource(…) 或者 updateFromImportSource(…) 抛出一个错误来删除所有对象。
获取和查询
CoreStore中获取和查询是分开的:
获取的过程是从指定事务或者数据栈中进行搜索,这表示fetch包含未提交的数据(transaction 调用 commit 之前)在下面几种情况下使用 fetch
结果要是 NSManagedObeject 实例
搜索结果要包含未保存的对象
查询直接从 persistent store 中获取数据,这意味着在计算总和最大最小的时候速度更快,使用场景:
需要使用聚集函数
结果是原始数据类型,例如NSStrings、NSNumbers、Ints、NSDates、键值对NSDictionary等
结果返回指定属性值
不包括未保存的对象
获取和查询的条件使用分句进行设置,必须要设置From 分句,知名目标实体类型。
let people = CoreStore.fetchAll(From(MyPersonEntity))
// CoreStore.fetchAll(From&MyPersonEntity&()) works as well
let people = CoreStore.fetchAll(From(MyPersonEntity))// CoreStore.fetchAll(From&MyPersonEntity&()) works as well
上述例子中,people 是 MYPersonEntity 类型的数组。From(MyPersonEntity) 表示获取所有的 MyPersonEntity 对象。
如果该实体存在在多个配置中,需要指明目标 persistent store 的配置名。
let people = CoreStore.fetchAll(From&MyPersonEntity&("Config1")) // ignore objects in persistent stores other than the "Config1" configuration
let people = CoreStore.fetchAll(From&MyPersonEntity&("Config1")) // ignore objects in persistent stores other than the "Config1" configuration
或者使用 nil 表示默认的配置名。
let person = CoreStore.fetchAll(From&MyPersonEntity&(nil))
let person = CoreStore.fetchAll(From&MyPersonEntity&(nil))
现在已经知道怎么使用 From 了。
CoreStore 有五种,可以通过 DataStack 实例或者 BaseDataTransaction 实例用来获取的方法。这些方法所需参数都相同:必须要有一个 From 分句,可选的有Where、OrderBy和Tweak分句:
fetchAll(…) :返回所有符合要求的值
fetchOne(…) :返回符合条件的第一个值
fetchCount(…) :返回符合条件值的数量
fetchObjectIDs(…):返回一个包含所有符合条件对象的NSManagedObjectID的队列
fetchObjectID(…):返回符合条件的第一个值的NSManagedObjectID
每个方法的目的都很直接,下面我们来介绍这些分句。
Where 分句
Where 分句是对 NSPredicate 的包装,是获取的过滤器。除了CoreData不支持的 -predicateWithBlock:,其他初始化和NSPredicate一样。
var people = CoreStore.fetchAll(
From(MyPersonEntity),
Where("%K & %d", "age", 30) // string format initializer
people = CoreStore.fetchAll(
From(MyPersonEntity),
Where(true) // boolean initializer
var people = CoreStore.fetchAll(&&&&From(MyPersonEntity),&&&&Where("%K & %d", "age", 30) // string format initializer)people = CoreStore.fetchAll(&&&&From(MyPersonEntity),&&&&Where(true) // boolean initializer)
如果你有一个 NSPredicate 实例,你可以将其传入 Where 分句:
let predicate = NSPredicate(...)
var people = CoreStore.fetchAll(
From(MyPersonEntity),
Where(predicate) // predicate initializer
let predicate = NSPredicate(...)var people = CoreStore.fetchAll(&&&&From(MyPersonEntity),&&&&Where(predicate) // predicate initializer)
Where 分句还支持 && || !这些逻辑运算,可以替代 AND、OR、NOT这些逻辑。
var people = CoreStore.fetchAll(
From(MyPersonEntity),
Where("age & %d", 30) && Where("gender == %@", "M")
var people = CoreStore.fetchAll(&&&&From(MyPersonEntity),&&&&Where("age & %d", 30) && Where("gender == %@", "M"))
如果不调用 Where 分句,所有 From 条件下的对象都会被返回。
OrderBy 分句
OrderBy 是对 NSSortDescriptor 的封装,根据指定属性值进行排序。
var mostValuablePeople = CoreStore.fetchAll(
From(MyPersonEntity),
OrderBy(.Descending("rating"), .Ascending("surname"))
var mostValuablePeople = CoreStore.fetchAll(&&&&From(MyPersonEntity),&&&&OrderBy(.Descending("rating"), .Ascending("surname")))
OrderBy 中接收一连串 SortKey 枚举值,可以是 .Ascending 也可以是 .Descending。
你可以使用 + 和 += 运算符将两个 OrderBy 组合。
var orderBy = OrderBy(.Descending("rating"))
if sortFromYoungest {
orderBy += OrderBy(.Ascending("age"))
var mostValuablePeople = CoreStore.fetchAll(
From(MyPersonEntity),
var orderBy = OrderBy(.Descending("rating"))if sortFromYoungest {&&&&orderBy += OrderBy(.Ascending("age"))}var mostValuablePeople = CoreStore.fetchAll(&&&&From(MyPersonEntity),&&&&orderBy)
Tweak 分句
Tweak 闭包中暴露一个 AFetchRequest 值,让你可以改变属性值。
var people = CoreStore.fetchAll(
From(MyPersonEntity),
Where("age & %d", 30),
OrderBy(.Ascending("surname")),
Tweak { (fetchRequest) -& Void in
fetchRequest.includesPendingChanges = false
fetchRequest.returnsObjectsAsFaults = false
fetchRequest.includesSubentities = false
12345678910
var people = CoreStore.fetchAll(&&&&From(MyPersonEntity),&&&&Where("age & %d", 30),&&&&OrderBy(.Ascending("surname")),&&&&Tweak { (fetchRequest) -& Void in&&&&&&&&fetchRequest.includesPendingChanges = false&&&&&&&&fetchRequest.returnsObjectsAsFaults = false&&&&&&&&fetchRequest.includesSubentities = false&&&&})
分句的执行时根据其在 fetch/query 中的顺序,因此一般你要把 Tweak 分句放在最后。
Tweak 闭包就是获取命令之前执行,所以要确保闭包中占用的任何值不存在竞争状态。
Tweak 只是让你最小程度上地对 NSFetchRequest 进行配置。记住,CoreStore 已经帮你预配置 NSFetchRequest 最合适的默认状态。当你对自己在做什么了解的时候才能使用Tweak。
Core Data 的包装中所忽视的一个功能就是获取原始值。如果你对 NSDictionaryResultType 和 -[NSFetchedRequest propertiesToFetch] 比较熟悉,你就会知道ss获取原始值和聚合值的查询是多么麻烦。CoreStore通过暴露下面两个方法解决了这个问题:
queryValue(…) :然后属性或者聚合值的原始值,如果有多个值只返回第一个。
queryAttributes(…) :返回一个队列其中包含的字典组成是属性key和相应的值。
上面的方法的参数都相同。一个必须的 From 分句,可选的 Select&T& 分句和 Where、OrderBy、GroupBy 或者 Tweak 分句。
From、Where、OrderBy 和 Tweak 分句与上面获取一样,接下来介绍 Select&T& 分句和 GroupBy 分句。
Select&T& 分句
该分句指定返回值类型和 key 属性:
let johnsAge = CoreStore.queryValue(
From(MyPersonEntity),
Select&Int&("age"),
Where("name == %@", "John Smith")
let johnsAge = CoreStore.queryValue(&&&&From(MyPersonEntity),&&&&Select&Int&("age"),&&&&Where("name == %@", "John Smith"))
上面的例子查询的是 “age” 这个属性,返回值是第一个符合条件的对象。johnsAge 会被界定在 Int 类型中,正如 Select&Int& 所示。在queryValue(…) 中以下是可设置的返回类型:
NSDecimalNumber
NSManagedObjectID
对于 queryAttributes(…),Select 只能返回 NSDictionary,因此可以省略类型。
let allAges = CoreStore.queryAttributes(
From(MyPersonEntity),
Select("age")
let allAges = CoreStore.queryAttributes(&&&&From(MyPersonEntity),&&&&Select("age"))
如果你只需要特殊特征值,下面几个聚合函数可以作为参数传入 Select:
.Average(…)
.Count(…)
.Maximum(…)
.Minimum(…)
let oldestAge = CoreStore.queryValue(
From(MyPersonEntity),
Select&Int&(.Maximum("age"))
let oldestAge = CoreStore.queryValue(&&&&From(MyPersonEntity),&&&&Select&Int&(.Maximum("age")))
queryAttributes(…) 返回的是字典组成的数组,你可以在 Select 中指定多个属性:
let personJSON = CoreStore.queryAttributes(
From(MyPersonEntity),
Select("name", "age")
let personJSON = CoreStore.queryAttributes(&&&&From(MyPersonEntity),&&&&Select("name", "age"))
上面的代码返回:
"name": "John Smith",
"name": "Jane Doe",
12345678910
[&&&&[&&&&&&&&"name": "John Smith",&&&&&&&&"age": 30&&&&],&&&&[&&&&&&&&"name": "Jane Doe",&&&&&&&&"age": 22&&&&]]
还可以包含聚合函数:
let personJSON = CoreStore.queryAttributes(
From(MyPersonEntity),
Select("name", .Count("friends"))
let personJSON = CoreStore.queryAttributes(&&&&From(MyPersonEntity),&&&&Select("name", .Count("friends")))
返回值如下:
"name": "John Smith",
"count(friends)": 42
"name": "Jane Doe",
"count(friends)": 231
12345678910
[&&&&[&&&&&&&&"name": "John Smith",&&&&&&&&"count(friends)": 42&&&&],&&&&[&&&&&&&&"name": "Jane Doe",&&&&&&&&"count(friends)": 231&&&&]]
count(friends) 你还可以用别名:
let personJSON = CoreStore.queryAttributes(
From(MyPersonEntity),
Select("name", .Count("friends", As: "friendsCount"))
let personJSON = CoreStore.queryAttributes(&&&&From(MyPersonEntity),&&&&Select("name", .Count("friends", As: "friendsCount")))
这个的返回值:
"name": "John Smith",
"friendsCount": 42
"name": "Jane Doe",
"friendsCount": 231
12345678910
[&&&&[&&&&&&&&"name": "John Smith",&&&&&&&&"friendsCount": 42&&&&],&&&&[&&&&&&&&"name": "Jane Doe",&&&&&&&&"friendsCount": 231&&&&]]
GroupBy 分句
GroupBy 分句将结果按照所指定的属性进行分组,该分句只可用于 queryAttributes(…),因为 queryValue(…) 值返回第一个值:
let personJSON = CoreStore.queryAttributes(
From(MyPersonEntity),
Select("age", .Count("age", As: "count")),
GroupBy("age")
let personJSON = CoreStore.queryAttributes(&&&&From(MyPersonEntity),&&&&Select("age", .Count("age", As: "count")),&&&&GroupBy("age"))
这段代码返回以下字典格式,显示每个 “age” 属性的数量:
"age": 42,
"count": 1
"age": 22,
"count": 1
12345678910
[&&&&[&&&&&&&&"age": 42,&&&&&&&&"count": 1&&&&],&&&&[&&&&&&&&"age": 22,&&&&&&&&"count": 1&&&&]]
日志记录和错误处理
在使用第三方库的时候,一个特别容易不爽的点就是控制台的输出依照的是他们特有的日志机制。CoreStore提供了默认的日志类,但是同时你还可以用过实现 CoreStoreLogger 进行自定义。
final class MyLogger: CoreStoreLogger {
func log(#level: LogLevel, message: String, fileName: StaticString, lineNumber: Int, functionName: StaticString) {
// pass to your logger
func handleError(#error: NSError, message: String, fileName: StaticString, lineNumber: Int, functionName: StaticString) {
// pass to your logger
func assert(@autoclosure condition: () -& Bool, message: String, fileName: StaticString, lineNumber: Int, functionName: StaticString) {
// pass to your logger
12345678910111213
final class MyLogger: CoreStoreLogger {&&&&func log(#level: LogLevel, message: String, fileName: StaticString, lineNumber: Int, functionName: StaticString) {&&&&&&&&// pass to your logger&&&&}&&&&&func handleError(#error: NSError, message: String, fileName: StaticString, lineNumber: Int, functionName: StaticString) {&&&&&&&&// pass to your logger&&&&}&&&&&func assert(@autoclosure condition: () -& Bool, message: String, fileName: StaticString, lineNumber: Int, functionName: StaticString) {&&&&&&&&// pass to your logger&&&&}}
然后将这个类的实例传给CoreStore:
CoreStore.logger = MyLogger()
CoreStore.logger = MyLogger()
为了保持调用栈信息的完整,都没有进行线程管理。因此你要确保你的 logger 是线程安全的或者将日志实现分配到串行队列。
观察改变和本地通知
ObjectMonitor:用来监视单个 NSManagedObject 实例的变化(替代KVO)
ListMonitor:用来监视一连串 NSManagedObject 实例的变化(替代NSFetchedResultsController)
观察单一对象
为了观察某一对象,需要实现 ObjectObserver 协议并且指明 EntityType。
class MyViewController: UIViewController, ObjectObserver {
func objectMonitor(monitor: ObjectMonitor&MyPersonEntity&, willUpdateObject object: MyPersonEntity) {
func objectMonitor(monitor: ObjectMonitor&MyPersonEntity&, didUpdateObject object: MyPersonEntity, changedPersistentKeys: Set&KeyPath&) {
func objectMonitor(monitor: ObjectMonitor&MyPersonEntity&, didDeleteObject object: MyPersonEntity) {
12345678910111213
class MyViewController: UIViewController, ObjectObserver {&&&&func objectMonitor(monitor: ObjectMonitor&MyPersonEntity&, willUpdateObject object: MyPersonEntity) {&&&&&&&&// ...&&&&}&&&&&func objectMonitor(monitor: ObjectMonitor&MyPersonEntity&, didUpdateObject object: MyPersonEntity, changedPersistentKeys: Set&KeyPath&) {&&&&&&&&// ...&&&&}&&&&&func objectMonitor(monitor: ObjectMonitor&MyPersonEntity&, didDeleteObject object: MyPersonEntity) {&&&&&&&&// ...&&&&}}
然后我们需要声明一个 ObjectMonitor 实例,然后将我们的 ObjectObserver 注册为观察者。
let person: MyPersonEntity = // ...
self.monitor = CoreStore.monitorObject(person)
self.monitor.addObserver(self)
let person: MyPersonEntity = // ...self.monitor = CoreStore.monitorObject(person)self.monitor.addObserver(self)
当该对象的属性发生变化时,控制器就会通知我们的观察者。你可以向一个 objectMonitor 添加多个 ObjectObserver。这就意味着多个屏幕分享一个 ObjectMonitor 实例。
你可以通过对象特征来获取 ObjectMonitor 的对象,如果对象被删除,object 将会返回 nil。
同时 ObjectMonitor 也暴露了 removeObserver(…) 这个方法。它只储存弱引用的观察者,并且可以安全注销释放观察者。
观察对象序列
为了观察对象序列, 需要实现 ListObserver 协议并且指明 EntityType。
class MyViewController: UIViewController, ListObserver {
func listMonitorWillChange(monitor: ListMonitor&MyPersonEntity&) {
func listMonitorDidChange(monitor: ListMonitor&MyPersonEntity&) {
class MyViewController: UIViewController, ListObserver {&&&&func listMonitorWillChange(monitor: ListMonitor&MyPersonEntity&) {&&&&&&&&// ...&&&&}&&&&&func listMonitorDidChange(monitor: ListMonitor&MyPersonEntity&) {&&&&&&&&// ...&&&&}}
包括 ListObserver 在内,有三个你可以实现的观察者协议,你可以根据你的具体需求选择实现:
ListObserver:包含的回调方法。
func listMonitorWillChange(monitor: ListMonitor&MyPersonEntity&)
func listMonitorDidChange(monitor: ListMonitor&MyPersonEntity&)
func listMonitorWillChange(monitor: ListMonitor&MyPersonEntity&)&&&&&func listMonitorDidChange(monitor: ListMonitor&MyPersonEntity&)
ListObjectObserver:ListObserver的补充,可以用来处理对象的增加删除和插入事件。
func listMonitor(monitor: ListMonitor&MyPersonEntity&, didInsertObject object: MyPersonEntity, toIndexPath indexPath: NSIndexPath)
func listMonitor(monitor: ListMonitor&MyPersonEntity&, didDeleteObject object: MyPersonEntity, fromIndexPath indexPath: NSIndexPath)
func listMonitor(monitor: ListMonitor&MyPersonEntity&, didUpdateObject object: MyPersonEntity, atIndexPath indexPath: NSIndexPath)
func listMonitor(monitor: ListMonitor&MyPersonEntity&, didMoveObject object: MyPersonEntity, fromIndexPath: NSIndexPath, toIndexPath: NSIndexPath)
func listMonitor(monitor: ListMonitor&MyPersonEntity&, didInsertObject object: MyPersonEntity, toIndexPath indexPath: NSIndexPath)&&&&&func listMonitor(monitor: ListMonitor&MyPersonEntity&, didDeleteObject object: MyPersonEntity, fromIndexPath indexPath: NSIndexPath)&&&&&func listMonitor(monitor: ListMonitor&MyPersonEntity&, didUpdateObject object: MyPersonEntity, atIndexPath indexPath: NSIndexPath)&&&&&func listMonitor(monitor: ListMonitor&MyPersonEntity&, didMoveObject object: MyPersonEntity, fromIndexPath: NSIndexPath, toIndexPath: NSIndexPath)
ListSectionObserver:ListObjectObserver的补充,用来处理 section 的增加和删除。
func listMonitor(monitor: ListMonitor&MyPersonEntity&, didInsertSection sectionInfo: NSFetchedResultsSectionInfo, toSectionIndex sectionIndex: Int)
func listMonitor(monitor: ListMonitor&MyPersonEntity&, didDeleteSection sectionInfo: NSFetchedResultsSectionInfo, fromSectionIndex sectionIndex: Int)
func listMonitor(monitor: ListMonitor&MyPersonEntity&, didInsertSection sectionInfo: NSFetchedResultsSectionInfo, toSectionIndex sectionIndex: Int)&&&&&func listMonitor(monitor: ListMonitor&MyPersonEntity&, didDeleteSection sectionInfo: NSFetchedResultsSectionInfo, fromSectionIndex sectionIndex: Int)
然后创建一个 ListMonitor 实例并且将 ListObserver 注册为观察者。
self.monitor = CoreStore.monitorList(
From(MyPersonEntity),
Where("age & 30"),
OrderBy(.Ascending("name")),
Tweak { (fetchRequest) -& Void in
fetchRequest.fetchBatchSize = 20
self.monitor.addObserver(self)
self.monitor = CoreStore.monitorList(&&&&From(MyPersonEntity),&&&&Where("age & 30"),&&&&OrderBy(.Ascending("name")),&&&&Tweak { (fetchRequest) -& Void in&&&&&&&&fetchRequest.fetchBatchSize = 20&&&&})self.monitor.addObserver(self)
同样的,一个 ListMonitor 也可以注册多个 ListObserver。
你应该注意到了,monitorList(…) 方法支持Where、orderBy 和 Tweak 分句,和fetch一样。
ListMonitor 的观察对象需要有确切的顺序,至少 From 和 OrderBy 子句是需要实现的。
通过 monitorList(…) 创建的 ListMonitor 实例是单一 section 的 list,因此你可以如下获取其中的内容:
let firstPerson = self.monitor[0]
let firstPerson = self.monitor[0]
如果list需要分成多个section,则需要通过monitorSectionedList(…)创建ListMonitor,并实现SectionBy分句。
self.monitor = CoreStore.monitorSectionedList(
From(MyPersonEntity),
SectionBy("age"),
Where("gender", isEqualTo: "M"),
OrderBy(.Ascending("age"), .Ascending("name")),
Tweak { (fetchRequest) -& Void in
fetchRequest.fetchBatchSize = 20
self.monitor = CoreStore.monitorSectionedList(&&&&From(MyPersonEntity),&&&&SectionBy("age"),&&&&Where("gender", isEqualTo: "M"),&&&&OrderBy(.Ascending("age"), .Ascending("name")),&&&&Tweak { (fetchRequest) -& Void in&&&&&&&&fetchRequest.fetchBatchSize = 20&&&&})
以这种方式创建的 list 控制器会根据 SectionBy 分句中的属性值对对象进行分组。另外,OrderBy 的整理方式应该与 SectionBy 一样(与 NSFetchedResultsController 的原则相同)
SectionBy分句还可以通过闭包将section name转换成一个用于展示的字符串:
self.monitor = CoreStore.monitorSectionedList(
From(MyPersonEntity),
SectionBy("age") { (sectionName) -& String? in
"\(sectionName) years old"
OrderBy(.Ascending("age"), .Ascending("name"))
self.monitor = CoreStore.monitorSectionedList(&&&&From(MyPersonEntity),&&&&SectionBy("age") { (sectionName) -& String? in&&&&&&&&"\(sectionName) years old"&&&&},&&&&OrderBy(.Ascending("age"), .Ascending("name")))
这在设置 TableView 的 header 上非常实用。
func tableView(tableView: UITableView, titleForHeaderInSection section: Int) -& String? {
let sectionInfo = self.monitor.sectionInfoAtIndex(section)
// sectionInfo is an NSFetchedResultsSectionInfo instance
return sectionInfo.name
func tableView(tableView: UITableView, titleForHeaderInSection section: Int) -& String? {&&&&let sectionInfo = self.monitor.sectionInfoAtIndex(section)&&&&// sectionInfo is an NSFetchedResultsSectionInfo instance&&&&return sectionInfo.name}
使用 NSIndexPath 或者元组获取对象。
let indexPath = NSIndexPath(forRow: 2, inSection: 1)
let person1 = self.monitor[indexPath]
let person2 = self.monitor[1, 2]
// person1 and person2 are the same object
let indexPath = NSIndexPath(forRow: 2, inSection: 1)let person1 = self.monitor[indexPath]let person2 = self.monitor[1, 2]// person1 and person2 are the same object
支持iCloud存储
CoreSpotlight能进行自动索引(实验性的)
需要: iOS 7及以上、Swift2.2(Xcode 7.3)
依赖库: GCDKit
使用CocoaPods安装(不支持iOS7)
pod 'CoreStore'
pod 'CoreStore'
这里讲CoreStore作为一个框架进行安装。在你的 swift 文件中 import CoreStore 来使用这个库。
使用Carthage安装
在你的Cartfile中添加
github "JohnEstropia/CoreStore" &= 1.6.0
github "JohnEstropia/GCDKit" &= 1.2.2
github "JohnEstropia/CoreStore" &= 1.6.0&github "JohnEstropia/GCDKit" &= 1.2.2
carthage update
carthage update
使用Git Submodule安装
git submodule add https://github.com/JohnEstropia/CoreStore.git &destination directory&
git submodule add https://github.com/JohnEstropia/CoreStore.git &destination directory&
将 CoreStore.xcodeproj 拖放入你的工程
作为一个框架进行安装(iOS7不支持)
将 CoreStore.xcodeproj 拖放入你的工程
直接加入你的 app 模块
将所有的 swift 文件添加进你的工程
v0.2.0到1.0.0的一些变化
重新命名了一些类和协议,名字变得更短、关联性更强、更便于记忆:
ManagedObjectController 改为 ObjectMonitor
ManagedObjectObserver 改为 ObjectObserver
ManagedObjectListController 改为 ListMonitor
ManagedObjectListChangeObserver 改为 ListObserver
ManagedObjectListObjectObserver 改为 ListObjectObserver
ManagedObjectListSectionObserver 改为 ListSectionObserver
SectionedBy 改为 SectionBy(与OrderBy和GroupBy更匹配),为了保留自然语言语义,以上这些协议中的方法也进行了重命名。
有些方法取消了返回enum结果,取而代之的是抛出错误
新的迁移机制!请查看DataStack+Migration.swift和CoreStore+Migration.swift中的新方法,以及DataStack.swift新的初始化方法。
资源整理者简介:
可能感兴趣的文章
按分类快速查找
关于资源导航
伯乐在线资源导航收录优秀的工具资源。内容覆盖开发、设计、产品和管理等IT互联网行业相关的领域。目前已经收录 1440 项工具资源。
关于资源导航
伯乐在线资源导航收录优秀的工具资源。内容覆盖开发、设计、产品和管理等IT互联网行业相关的领域。
新浪微博:
推荐微信号
(加好友请注明来意)
- 好的话题、有启发的回复、值得信赖的圈子
- 分享和发现有价值的内容与观点
- 为IT单身男女服务的征婚传播平台
- 优秀的工具资源导航
- 翻译传播优秀的外文文章
- 国内外的精选博客文章
- UI,网页,交互和用户体验
- 专注iOS技术分享
- 专注Android技术分享
- JavaScript, HTML5, CSS
- 专注Java技术分享
- 专注Python技术分享
& 2018 伯乐在线

我要回帖

更多关于 不特定对象 的文章

 

随机推荐