注:本文来自Core Data by tutorials 2.0 , swift + iOS 9 .本文非翻译 只讲大体思路
到目前为止,我们一直在依赖xcode自动创建的coredata模板,这没什么问题,但是如果真的想知道CoreData是怎么工作的,我们需要自己写代码创建对象
这里会涉及到四个Core Data的类:
- NSManagedObjectModel
- NSPersistentStore
- NSPersistentStoreCoordinator
- NSManagedObjectContext
这四个类,我们前面只遇到过NSManagedObjectContext
本小结将会学习这四个类的细节
The managed object model--NSManagedObjectModel
NSManagedObjectModel
代表你app中的每个数据对象类型,以及他们的属性和他们之间的关系。 上一章我们已经使用他创建对象保存属性和数据。
我们应该把NSManagedObjectModel
当做一个数据模型,NSManagedObjectModel
底层使用的是sqllite
sqllite只是CoreData可用的持久化类型之一,后面会详细介绍
我们用Xcode提供的visual editor创建/编辑了一个xcdatamodel file,然而真正在幕后的是一个叫momc的编译器(compiler),把model file编译后的结果放到momd文件夹下。Core Data可以很高效地在运行时使用momd文件夹里编译过的内容,来初始化一个NSManagedObjectModel实例。
持久化存储
Core Data提供了四种开箱即用的NSPersistentStore存储类型,三种原子型atomic的,一种非原子型non-atomic的
一个原子性存储在你进行任何读写操作之前需要完全的反序列化比不过且加载到内存中,相反一个非原子性的存储可以在需要的时候一块块的把自己加载到内存。
- NSQLiteStoreType 依托SQLite数据库,也是唯一的非原子型的non-atomic
- NSXMLStoreType 依托于XML文件,是原子型的atomic NSXMLStoreType只在os x可用
- NSBinaryStoreType 依托于二进制文件,是原子型的atomic
- NSInMemoryStoreType 其实是存在于内存中的persistent store type,不算严格意义上的持久化存储,通常用来做单元测试和缓存。
只要你的数据类型是基于JSON和CSV的格式,你还可以通过创建NSIncrementalStore的子类来创建自己的persistent store类型。
The persistent store coordinator
NSPersistentStoreCoordinator
是 NSManagedObjectModel
和 NSPersistentStore
的桥梁。他负责理解model,更好地来处理信息的存取。特别是有多个persistent stores时,persistent store coordinator相对managed context提供唯一的接口,保证了context与特定的persistent store交互。
The managed object context
之前的章节提到过context可以看做是内存中的暂存器,来记录所有的managed object的行为,当然managed object所做的任何改变,在context没有save()之前,都是不会在数据库中生效的。
关于context的五个比较重要的特性:
The context 管理着对象们的生命周期,不管这些对象是create还是fetch到的,这种对生命周期的管理在faulting、inverse、relationship handling 和 validation时很有用。
A managed object不能脱离context而存在,他们是紧紧地绑在一起的。
contexts都很有领土意识,一旦一个managed object被归到某个context中去了,那么这个managed object在他整个生命周期内属于这个context了。
一个应用可以使用多个context,少数的Core Data应用都这么搞。
一个 context是非线程安全的,你最好不要跨线程去使用context,Apple提供了多种方式来在多线程中使用context,后面会讲到。
创建自己的stack对象
作者带我们创建了一个CoreDataStack
对象
完整版如下:
import CoreData
class CoreDataStack{
let modelName = "Dog Walk"
private lazy var applicationDocumentDirectiry:NSURL = {
let urls = NSFileManager.defaultManager().URLsForDirectory(
.DocumentDirectory, inDomains: .UserDomainMask)
return urls[urls.count-1]
}()
lazy var context: NSManagedObjectContext = {
var managedObjectContext = NSManagedObjectContext(
concurrencyType: .MainQueueConcurrencyType)
managedObjectContext.persistentStoreCoordinator = self.psc
return managedObjectContext
}()
private lazy var psc: NSPersistentStoreCoordinator = {
let coordinator = NSPersistentStoreCoordinator(
managedObjectModel: self.managedObjectModel)
let url = self.applicationDocumentDirectiry.URLByAppendingPathComponent(self.modelName)
do {
let options = [NSMigratePersistentStoresAutomaticallyOption : true]
try coordinator.addPersistentStoreWithType(
NSSQLiteStoreType, configuration: nil, URL: url,
options: options)
} catch {
print("Error adding persistent store.")
}
return coordinator
}()
private lazy var managedObjectModel: NSManagedObjectModel = {
let modelURL = NSBundle.mainBundle()
.URLForResource(self.modelName,
withExtension: "momd")!
return NSManagedObjectModel(contentsOfURL: modelURL)!
}()
func saveContext(){
if self.context.hasChanges{
do {
try self.context.save()
} catch let error as NSError {
print("Error: \(error.localizedDescription)")
abort()
}
}
}
}
简单的看其实就是这样
class CoreDataStack
{
let context:NSManagedObjectContext
let psc:NSPersistentStoreCoordinator
let model:NSManagedObjectModel
let store:NSPersistentStore?
let modelName:String
func saveContext(){}
}
上文提到的四个属性掌握好就行了,下面解释下
首先导入CoreData库,加入modelName,(因为我们的项目并没有勾选use core data,管理对象模型需要我们自己创建,这个name就是对应的那个)。
applicationDocumentsDirectory
返回我们app,document目录的一个懒加载属性。
其他四个每个类型都对应一个Core Data stack的组成部分。他们之间互相依赖并且都使用了懒加载
唯一外面可用的属性就是NSManagedObjectContext
最后提供了保存的快捷方法
然后转到ViewController.swift ,导入CoreData
声明一个var managedContext: NSManagedObjectContext!
打开AppDelegate.swift
1、import CoreData
2、lazy var coreDataStack = CoreDataStack()
3、
let navigationController =
window!.rootViewController as! UINavigationController
let viewController =
navigationController.topViewController as! ViewController
viewController.managedContext = coreDataStack.context
4、
//确保app在进入后台 或者 终端前 能保存待定数据
func applicationDidEnterBackground(application: UIApplication) {
coreDataStack.saveContext()
}
func applicationWillTerminate(application: UIApplication) {
coreDataStack.saveContext()
}
Modeling your data
我们这次是没有.xcdatamodel文件的 需要自己创建
1、File\New\File…
2、iOS\Core Data\Data Model
3、Next
4、命名Dog Walk,(对应之前的modelName)
5、Create
根据下图 创建一个Entity
然后创建第二个Entity
这里Dog和Walk是一对多的关系(用过数据库都知道)
所以这里要添加一个relationships
在Dog中添加一个walks
在旁边的属性查看器中修改,type为one to many,勾选order
在Walk中也建立一个relationship
切换下Editor Style,(右下角)
这个图清楚的表现出他们之间的关系
添加managed object的子类
- Editor\Create NSManagedObject Subclass…
- Dog Walk
- 勾选Dog 和 Walk
创建出两个NSManagedObject的子类,就像第二章学过的那样
import Foundation
import CoreData
extension Dog {
@NSManaged var name: String?
@NSManaged var walks: NSOrderedSet?
}
import Foundation
import CoreData
extension Walk {
@NSManaged var date: NSDate?
@NSManaged var dog: Dog?
}
然后 回到ViewController中声明var currentDog: Dog!
然后 在viewDidLoad中加上以下代码
let dogEntity = NSEntityDescription.entityForName("Dog", inManagedObjectContext: managedContext)
let dogName = "laal"
let dogFetch = NSFetchRequest(entityName: "Dog")
dogFetch.predicate = NSPredicate(format: "name == %@",dogName)
do {
let result = try managedContext.executeFetchRequest(dogFetch) as! [Dog]
if result.count>0{
currentDog = result.first
}else{
currentDog = Dog(entity: dogEntity!,
insertIntoManagedObjectContext: managedContext)
currentDog.name = dogName
try managedContext.save()
}
} catch let error as NSError {
print("Error: \(error.localizedDescription)")
}
以上代码取出所有名字为"laal"的Dog如果没有 就创建一个加到数据库中 然后 保存
然后就是替换tableview的一些操作
func tableView(tableView: UITableView,
numberOfRowsInSection section: Int) -> Int {
return currentDog.walks!.count
}
func tableView(tableView: UITableView,
cellForRowAtIndexPath
indexPath: NSIndexPath) -> UITableViewCell {
let cell =
tableView.dequeueReusableCellWithIdentifier("Cell",
forIndexPath: indexPath) as UITableViewCell
let walk = currentDog.walks![indexPath.row] as! Walk
cell.textLabel!.text = dateFormatter.stringFromDate(walk.date!)
return cell
}
add中 修改如下
@IBAction func add(sender: AnyObject) {
let walkEntity = NSEntityDescription.entityForName("Walk",
inManagedObjectContext: managedContext)
let walk = Walk(entity: walkEntity!,
insertIntoManagedObjectContext: managedContext)
walk.date = NSDate()
//Insert the new Walk into the Dog's walks set
let walks = currentDog.walks!.mutableCopy() as! NSMutableOrderedSet
walks.addObject(walk)
currentDog.walks = walks.copy() as? NSOrderedSet
do {
try self.managedContext.save()
} catch let error as NSError {
print("Error: \(error.localizedDescription)")
}
tableView.reloadData()
}
作者还添加了删除功能
func tableView(tableView: UITableView,
canEditRowAtIndexPath indexPath: NSIndexPath) -> Bool {
return true
}
func tableView(tableView: UITableView,
commitEditingStyle
editingStyle: UITableViewCellEditingStyle,
forRowAtIndexPath indexPath: NSIndexPath) {
if editingStyle == UITableViewCellEditingStyle.Delete {
let walkToRemove = currentDog.walks![indexPath.row] as! Walk
managedContext.deleteObject(walkToRemove)
do {
try managedContext.save()
} catch let error as NSError {
print("Error: \(error.localizedDescription)")
}
//4
tableView.deleteRowsAtIndexPaths([indexPath],
withRowAnimation: UITableViewRowAnimation.Automatic)
}
}
这些代码很容易理解就不多赘述了