概述
以下是需要明白的概念:
- Persistent store: 固态存储,表示存储在Nand flash的真实数据库,我们不会之间用这个对象
- Persistent store coordinator: 固态存储协调者,负责协调从固态存储里面读写数据,是managed object context和固态存储的桥梁
- Managed object model (MOM):一个简单的存在磁盘上的文件,代表我们的数据模型
- Managed object: 代表了我们希望存在Core Data里面的entity,对应于一般的数据库而言,entity就是table的概念,类型是NSManagedObject,由Managed object context (MOC)存储,构成Managed object model (MOM),通过Persistent store coordinator存储在Persistent store里面
- Managed object context (MOC): 当你建立了一个Core data对象,设置属性,对他进行操作。所有的这些都有MOC完成。
17.1 批量更新数据库
用NSBatchUpdateRequest,步骤:
1. 声明一个NSBatchUpdateRequest
2. 然后要写断言,predicate
3. 执行,用managedObjectContext的executeRequest:error:
let batch = NSBatchUpdateRequest(entityName: entityName)
batch.propertiesToUpdate = ["age" : 18]
batch.predicate = NSPredicate(format: "age < %@", 18 as NSNumber)
batch.resultType = .UpdatedObjectsCountResultType
var batchError: NSError?
let result = managedObjectContext!.executeRequest(batch, error: &batchError)
if result != nil {
if let theResult = result as? NSBatchUpdateResult {
if let numberOfAffectedPersons = theResult.result as? Int {
println("result is \(numberOfAffectedPersons)")
}
}
} else {
if let error = batchError {
println("Could not perform batch request. Error = \(error)")
}
}
17.2 写到Core Data
用NSEntityDescription的insertNewObjectForEntityForName:inManagedObjectContext:, 先要分配一个entity的对象,然后对对象进行修改,然后在存进去,逻辑有些奇怪。其实是在没有save之前,都没有对磁盘里面的数据库进行操作,是对缓存里的进行操作。任何对数据库有修改的操作,都需要save操作。
let entityName = NSStringFromClass(Person.classForCoder())
func populateDatabase() {
for counter in 0..<1000 {
let person = NSEntityDescription.insertNewObjectForEntityForName(entityName, inManagedObjectContext: managedObjectContext!) as! Person
person.firstName = "First name \(counter)"
person.lastName = "Last name \(counter)"
person.age = NSNumber(unsignedInt: arc4random_uniform(120))
}
var savingError: NSError?
if managedObjectContext!.save(&savingError) {
println("save to database")
} else {
if let error = savingError {
println("Failed to save to database Error = \(error)")
}
}
}
17.3 从Core Data读数据
用NSFetchRequest,先新建一个fetchRequest,然后再执行fetch操作。
let fetchRequest = NSFetchRequest(entityName: entityName)
fetchRequest.predicate = NSPredicate(format: "age > %@", 18 as NSNumber)
var requestError: NSError?
let persons = managedObjectContext!.executeFetchRequest(fetchRequest, error: &requestError) as! [Person!]
if persons.count > 0 {
var count = 1
for person in persons {
println("Person \(count) first name = \(person.firstName)")
println("Person \(count) last name = \(person.lastName)")
println("Person \(count) age = \(person.age)")
count++
}
} else {
println("Could not find any Person")
}
17.4 删除数据
用NSManagedObjectContext的deleteObject, 然后再save,这个逻辑也满怪的。理由同17.2
if persons.count > 0 {
let lastPerson = persons.last
managedObjectContext!.deleteObject(lastPerson!)
var savingError:NSError?
if managedObjectContext!.save(&savingError) {
println("deleted")
} else {
if let error = savingError {
println("fail to delete last person. Error = \(error)")
}
}
}
17.5 排序
创建NSSortDescriptor,然后执行fetch操作
let fetchRequest = NSFetchRequest(entityName: "Person")
let ageSort = NSSortDescriptor(key: "age", ascending: true)
let firstNameSort = NSSortDescriptor(key: "firstName", ascending: true)
fetchRequest.sortDescriptors = [ageSort,firstNameSort]
var requestError:NSError?
let persons = managedObjectContext!.executeFetchRequest(fetchRequest, error: &requestError) as! [Person!]
for person in persons {
println("First name = \(person.firstName)")
println("Second name = \(person.lastName)")
println("Age = \(person.age)")
}
17.6 在TableView里面显示Core data数据
用NSFetchedResultsController, 其实就是取代17.5讲的fetch,用NSFetchedResultsController来执行fetch操作,从而由iOS来解决一次取多少,如何重复取的问题。下面是他的一些重要的概念:
- sections:属性,类型是NSArray,通过key path(sectionNameKeyPath)来归类数据;
- objectAtIndexPath: 实例方法,返回一个managed object,也就是一个attitude对象
- fetchRequest: 属性,NSFetchRequest对象
把fetch的操作交给NSFetchedResultsController去做,其他的部分是和前面的一样的
let fetchRequest = NSFetchRequest(entityName: "Person")
let ageSort = NSSortDescriptor(key: "age", ascending: true)
let firstNameSort = NSSortDescriptor(key: "firstName", ascending: true)
fetchRequest.sortDescriptors = [ageSort,firstNameSort]
frc = NSFetchedResultsController(fetchRequest: fetchRequest, managedObjectContext: managedObjectContext!, sectionNameKeyPath: nil, cacheName: nil)
frc.delegate = self
var fetchingError: NSError?
if frc.performFetch(&fetchingError) {
println("SuccessFully fetched")
} else {
println("Failed to fetch")
}
以下是NSFetchedResultsControllerDelegate的一些重要的delegate,用于通知fetch request执行的过程点:
- controllerWillChangeContent:当managedObjectContext将要改变的时候,常常是在另外一个类里面被改变,需要准备重新刷新table view,用tableview的beginUpdates方法
- controllerDidChangeContent: 当managedObjectContext已经被刷新了,需要在这个方法里面调用tableview的endUpdates,通知table view结束刷新。
- controller:didChangeObject:atIndexPath:forChangeType:newIndexPath: 当某一个特定的操作发生的时候,比如如果删除一个对象,forChangeType会包含NSFetchedResultsChangeDelete,如果插入了一个对象,会包含NSFetchedResultsChangeInsert,如果有对象被更新,也会调用这个函数
17.7 Core Data 的关系
Coredata的relationship可以是一对一,一对多,多对多
Coredata里面的一对一关系比较特殊,A和B,一对一,A可以知道B,但是B不知道A
t.b.d
17.8 后台取数据
注意managed object并不是线程安全的,不要在不同的线程之间使用,例如:你如果在后台取了managed object,那么不能在主线程直接使用这些managed object,而应该在主线程用objectWithID来取。objectWithID接受NSManagedObjectID的对象,所以在你的后台线程中,其实只需要取得ID,然后把ID给主线程。
步骤:
- 用NSManagedObjectContext的初始化函数创建一个background context,使用NSPrivateQueueConcurrencyType参数,会返回一个由自己私有dispatch队列的context。
- 设置persistentStoreCoordinator
- performBlock ,取得id
- 跳回主线程,取得managed object
let backgroundContext = NSManagedObjectContext(concurrencyType: NSManagedObjectContextConcurrencyType.PrivateQueueConcurrencyType)
backgroundContext.persistentStoreCoordinator = persistentStoreCoordinator
backgroundContext.performBlock { [weak self]() -> Void in
let fetchRequest = NSFetchRequest(entityName: self!.entityName)
fetchRequest.fetchBatchSize = 20
fetchRequest.predicate = NSPredicate(format: "age > %@", 18 as NSNumber)
fetchRequest.resultType = .ManagedObjectIDResultType
var requestError: NSError?
let personIds = backgroundContext.executeFetchRequest(fetchRequest, error: &requestError) as! [NSManagedObjectID]
if requestError == nil {
dispatch_async(dispatch_get_main_queue(), {[weak self] () -> Void in
for personId in personIds {
let person = self!.managedObjectContext!.objectWithID(personId) as! Person
self!.mutablePersons.append(person)
}
println("get persons")
})
}else {
println("Fail to execute the fetch request")
}
17.9 自定义数据类型
如果你认为Core data提供的数据类型不能满足你的需要,比如,你想要UIColor。那么你可以自定义类型,用transformable属性。
t.b.d