3:Core Data的应用
托管物件内容(Managed Object Context)
托管物件模型(Managed Object Model)
持久性存储协调器(Persistent Store Coordinator)
持久性存储器(Persistent Store)
iOS10之前的
iOS10之后导入了一个名为
NSPersistentContainer的新类别(存取资料)来简化App中的CoreData
首先在AppDelegate中
添加
lazy var persistentContainer:NSPersistentContainer = {
// 以名为”FoodPin”的持久存储器来初始化
let container = NSPersistentContainer(name: "FoodPin")
container.loadPersistentStores(completionHandler: { (storeDescription, error) in
if let error = error as NSError? {
fatalError("Unresolved error \(error), \(error.userInfo)")
}
})
return container
}()
//提供资料存储的辅助方法(插入,删除,更新时使用)
func saveContext() {
let context = persistentContainer.viewContext
if context.hasChanges {
do{
try context.save()
}catch{
let nserror = error as NSError
fatalError("Unresolved error \(nserror), \(nserror.userInfo)")
}
}
}
然后建立一个资料模型DataModel它的后缀为FoodPin.xcdatamodeld
选择它来开启资料模型编辑器(Data Model Editor),可以建立实体(Entity)给资料模型
然后给实体添加字段及字段类型,其中image是Binary Data
建立托管物(RestaurantMO)
选取Restaurant实例,然后至资料模型检测器中看到Class区域,将Class命名为RestaurantMO
,然后设定Codegen选项为Class Definition,然后按下command+B按钮让Xcode帮你产生这个类
如果此时遇到错误,选取Product-> Clean再次按下command+B就可以了
存储
@IBAction func saveButtomTapped(_ sender: UIBarButtonItem) {
if let appDelegate = (UIApplication.shared.delegate as? AppDelegate) {
restaurant = RestaurantMO(context: appDelegate.persistentContainer.viewContext)
restaurant.name = nameTextField.text
restaurant.type = typeTextField.text
restaurant.location = addressTextField.text
restaurant.phone = phoneTextField.text
restaurant.summary = descriptionTextView.text
restaurant.isVisited = false
if let restaurantImage = photoImageView.image{
restaurant.image = UIImagePNGRepresentation(restaurantImage)
}
print("Saving data to context ...")
appDelegate.saveContext()
}
}
NSFetchResultController是特别设计用来处理Core Data读取请求后所回传的结果,并直接提供资料给表格视图,遵守NSFetchResultControllerDelegate协议
var fetchResultController:NSFetchedResultsController!
// 从资料存储器中读取资料(在viewDidLoad方法中写入)
// 先从RestaurantMO取得NSFetchRequest物件,使用NSSortDescriptor物件来指定排序,这里我们使用name作为键来指定RestaurantMO物件为升序排列
let fetchRequest:NSFetchRequest<RestaurantMO> = RestaurantMO.fetchRequest()
let sortDescriptor = NSSortDescriptor(key: "name", ascending: true)
fetchRequest.sortDescriptors = [sortDescriptor]
if let appDelegate = (UIApplication.shared.delegate as? AppDelegate){
let context = appDelegate.persistentContainer.viewContext
//读取请求见了完成之后,我们初始化fetchResultController,并指定委派来监听资料的变化情况.
fetchResultController = NSFetchedResultsController(fetchRequest: fetchRequest, managedObjectContext: context, sectionNameKeyPath: nil, cacheName: nil)
fetchResultController.delegate = self
do{
// 最后调用performFetch()方法来执行读取结果
try fetchResultController.performFetch()
if let fetchedObjects = fetchResultController.fetchedObjects{
// 存取fetchedObjects属性来取得RestaurantMO物件
restaurants = fetchedObjects
}
}catch{
print(error)
}
}
看NSFetchResultControllerDelegate协议:
controllerWillChangeContent(_:)
controller(_:didChange:at:for:newIndexPath)
controllerDidChangeContent(_:)
func controllerWillChangeContent(_ controller: NSFetchedResultsController<NSFetchRequestResult>) {
tableView.beginUpdates()
}
func controller(_ controller: NSFetchedResultsController<NSFetchRequestResult>, didChange anObject: Any, at indexPath: IndexPath?, for type: NSFetchedResultsChangeType, newIndexPath: IndexPath?) {
switch type {
case .insert:
if let newIndexPath = newIndexPath {
tableView.insertRows(at: [newIndexPath], with: .fade)
}
case .delete:
if let indexPath = indexPath {
tableView.deleteRows(at: [indexPath], with: .fade)
}
case .update:
if let indexPath = indexPath{
tableView.reloadRows(at: [indexPath], with: .fade)
}
default:
tableView.reloadData()
}
if let fetchedObjects = controller.fetchedObjects{
restaurants = fetchedObjects as! [RestaurantMO]
}
}
func controllerDidChangeContent(_ controller: NSFetchedResultsController<NSFetchRequestResult>) {
tableView.endUpdates()
}
// 删除 从资料存储器删除一列
if let appDelegate = (UIApplication.shared.delegate as? AppDelegate){
let context = appDelegate.persistentContainer.viewContext
let restaurantToDelete = self.fetchResultController.object(at: indexPath)
context.delete(restaurantToDelete)
appDelegate.saveContext()
}