> 原文出处: http://chengway.in/post/ji-zhu/core-data-by-tutorials-bi-ji
最近花了半个月读完了Raywenderlich家的[《Core Data by Tutorials》](http://www.raywenderlich.com/store/core-data-by-tutorials),接下来几天就做个回顾,笔记并不是对原书的简单翻译,算是对整个知识脉络的一个整理的过程吧:)
![](https://box.kancloud.cn/2015-08-21_55d6e71b40a4f.jpg)
**Update:** 上周去搞**Sketch**了,工具算是基本入门了,抄了几个图标,画了几个图,以后会再聊聊。好了,进入正题吧,今天打算介绍前三章,也是CoreData的最基础的部分。***
## **Chapter 1:Your First Core Data App**
第一章比较简单,主要是带你熟悉CoreData的用法,并用CoreData创建了一个简单的List APP,学完这章你能加满如下技能点:
* model data you want to store in Core Data using Xcode’s model editor;
* add new records to Core Data;
* fetch a set of records from Core Data;
* display the fetched results to the user in a table view.
第一章按顺序大致如下:
### **一、Getting started**
作者提供了一个Start Project,打开其实就是一个UITableViewController,你需要做的就是增加一个**addName**方法,这里使用了*UIAlertController*这个iOS 8新添加的方法来做数据的添加逻辑,使用起来也很优雅。
### **二、Modeling your data**
这个主要是根据你的数据模型创建对应的managed object model,主要操作也在*.xcdatamodeld*中进行,全GUI操作,没什么难度。
> You can think of a Core Data entity as a class “definition” and the managed object as an instance of that class.
> 这里作者用了面向对象的思想做了类比,虽然不是很准确,但也方便初学者理解。
### **三、Saving to Core Data**
这里要注意到NSManagedObject的“对象”其实是遵循**“KVC”**的,NSManagedObject对象的值改变后,在ManagedContext中进行save操作。
### **四、Fetching from Core Data**
创建一个NSFetchRequest对象,设置好各种条件,依旧是交给ManagedContext对象去执行。下面是作者描述了两种*fetch*失败的情况
> If there are no objects that match the fetch request’s criteria, the method returns an optional value containing an empty array.
> If an error occurred during the fetch, the method returns an optional value that contains nil. If this happens, you can inspect the NSError and respond appropriately.
**第一章到此结束:)**
* * *
## **Chapter 2:NSManagedObject Subclasses**
NSManagedObject上一章我们提到过,要存取属性,只能用KVC,使用起来既不安全也不符合面向对象的原则,所以我们这章要生成他的子类,来创建我们自己的属性,这样就又能愉快地使用**"."**语法啦。
第二章的大致顺序如下:
### **一、Getting started**
作者提供了一个**“挑选领结”**的Start Project,领结数据存放在plist文件中,同样是一个很简单的App。
### **二、Modeling your data**
打开xcdatamodeld文件进行编辑,添加属性。这里主要看一下**Binary Data**与**Transformable**两种类型:
* **Binary Data**这里将image对象保存成了二进制文件,当然任何可以序列化的对象其实都可以保存成二进制。这里作者强调了性能问题,如果将一个很大的二进制保存到SQLite数据库中很可能会产生卡顿等问题。当然,幸运的是Core Data提供了一种叫**Allows External Storage**的解决方式,他只对binary data的属性类型有效,你只需要简单的开启他就好了。
> When you enable Allows External Storage, Core Data heuristically decides on a per-value basis if it should save the data directly in the database or store a URI that points to a separate file.
* **Transformable**除了一些基本属性,如果一个对象遵循**NSCoding Protocol**,那么这个对象是可以选择使用transformable类型的,作者这里使用的是*UIColor*类型,遵循*NSSecureCoding*协议,而该协议又继承自*NSCoding*,所以设为Transformable是OK的。自定义的类想要设置为*Transformable*,那么你首先要实现**NSCoding protocol**
### **三、Managed object subclasses**
在Xcode中选择**Editor\Create NSManagedObject Subclass**创建managedObject对象的子类,这里注意下Map Model中的attribute type与实际子类对象中的属性的对应关系就好了
* String maps to String
* Integer 16/32/64, Float, Double and Boolean map to NSNumber
* Decimal maps to NSDecimalNumber
* Date maps to NSDate
* Binary data maps to NSData
* Transformable maps to AnyObject
当然如果你想保留Map Model中的原始基本类型,那么在创建**NSManagedObject Subclass**时要勾选*Use scalar properties for primitive data types*
> Similar to @dynamic in Objective-C, the @NSManaged attribute informs the Swift compiler that the backing store and implementation of a property will be provided at runtime instead of at compile time.
> 这里注意下两种语言的一点区别
创建完子类还要记得一点就是在Model中的**Attributes inspector**将*class name*设为你新生成的子类路径,完成这一步主要是为了在runtime时将managed object subclass与Model中的entity链接起来。(有点类似与在SB中创建一个VC,并设置他的custom class)。
### **四、Propagating a managed context**
因为Xcode默认的CoreData模板会将context在AppDelegate中创建,所以,就会有这种获取context的做法:
~~~
let appDelegate = UIApplication.sharedApplication().delegate as AppDelegate
~~~
但这种做法还是不推荐使用,较好的方法是能够在对象的的初始化中将context作为参数进行传递。
接下来,作者从plist中将数据通过子类对象导入到core data中,再对界面做了些操作,这都比较简单。
### **五、Data validation in Core Data**
数据校验这里可以在data model inspector 中设置最大最小值,如果出错会在context save的时候将错误信息传给error参数
**第二章到此结束:)**
* * *
## **Chapter 3:The Core Data Stack**
Core Data要正常运行,需要几个组件共同协作来完成,这些组件包括:
* NSManagedObjectModel
* NSPersistentStore
* NSPersistentStoreCoordinator
* NSManagedObjectContext
这些组件共同构成了Core Data Stack,组件间的关系我觉得用苹果官方提供的一张图来展示最好不过了
> ![](https://box.kancloud.cn/2015-08-21_55d6e71b5f9a9.jpg)
>
> * An external persistent store that contains saved records.
> * A persistent object store that maps between records in the store and objects in your application.
> * A persistent store coordinator that aggregates all the stores.
> * A managed object model that describes the entities in the stores.
> * A managed object context that provides a scratch pad for managed objects.
基本Core Data整个操作都是围绕这张图来做的,本章作者给出的例子是一个记录**狗狗?散步**的APP,按照时间顺序添加散步记录。
第三章的大致顺序如下:
### **一、The managed object model**
The NSManagedObjectModel 反映了APP中所有的对象类型在data model中所拥有的属性,以及对象之间的关系。作者还提到了我们用Xcode提供的visual editor创建/编辑了一个*xcdatamodel file*,然而真正在幕后的是一个叫*momc*的编译器(compiler),把model file编译后的结果放到**momd**文件夹下。Core Data可以很高效地在运行时使用*momd文件夹里编译过的内容*,来初始化一个**NSManagedObjectModel**实例。
### **二、The persistent store**
Core Data提供了四种开箱即用的NSPersistentStore存储类型,三种原子型*atomic*的,一种非原子型*non-atomic*的
> An atomic persistent store needs to be completely deserialized and loaded into memory before you can make any read or write operations. In contrast, a non- atomic persistent store can load chunks of itself onto memory as needed.
* NSQLiteStoreType 依托SQLite数据库,也是唯一的非原子型的*non-atomic*
* NSXMLStoreType 依托于XML文件,是原子型的*atomic*
* NSBinaryStoreType 依托于二进制文件,是原子型的*atomic*
* NSInMemoryStoreType 其实是存在于**内存中**的*persistent store type*,不算严格意义上的持久化存储,通常用来做单元测试和缓存。
除了上述介绍过的存储类型,作者说了,只要你的数据类型是基于JSON和CSV的格式,你还可以通过创建**NSIncrementalStore**的子类来创建自己的persistent store类型。
> [Incremental Store Programming Guide](https://developer.apple.com/library/ios/documentation/DataManagement/Conceptual/IncrementalStorePG/Introduction/Introduction.html)
### **三、The persistent store coordinator**
NSPersistentStoreCoordinator可以看成是一座连接managed object model与persistent store的桥梁,他负责理解model,更好地来处理信息的存取。特别是有多个persistent stores时,persistent store coordinator相对managed context提供唯一的接口,保证了context与特定的persistent store交互。
### **四、The managed object context**
之前的章节提到过context可以看做是内存中的scratchpad,来记录所有的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应用都这么搞。
⑤. A context是非线程安全的,你最好不要跨线程去使用context,Apple提供了多种方式来在多线程中使用context,后面会讲到。
~~~
### **五、Creating your stack object**
创建自己的core data stack,其实只要记住本章开始那张图,记住要创建四个对象以及他们之间的关系,创建起来还是比较简单的。
~~~
class CoreDataStack {
let context:NSManagedObjectContext
let psc:NSPersistentStoreCoordinator
let model:NSManagedObjectModel
let store:NSPersistentStore?
}
~~~
你要做到工作就是创建这些对象以及他们之间的关系,具体代码见苹果官方提供的[Snippet](https://developer.apple.com/library/ios/documentation/DataManagement/Conceptual/CoreDataSnippets/Articles/stack.html)
创建完毕,使用起来也相当简单,这里注意使用**Lazy**来懒加载
~~~
lazy var coreDataStack = CoreDataStack()
~~~
### **六、Modeling your data**
接下来就是在Xcode里创建model,这里也没什么难度。增加两个Entity,设置他们的关系为一对多,并在一对多的关系下面的Arrangement那里勾选Ordered
![](https://box.kancloud.cn/2015-08-21_55d6e71bc3146.jpg)
### **七、Adding managed object subclasses**
这里通过Xcode生成的子类要注意一下,刚才一对多的关系因为上图勾选了Ordered,所以这里生成了一个NSOrderedSet类型的对象。*一开始我“以为这个真是一个有序集合,CoreData会为这个集合里的子对象们排序”*,但经过和[@Kyrrr](http://www.weibo.com/u/2626996387)同学讨论试验,发现并不是这么一回事。真正的原因是**集合里的子对象们都是无序的,本来应该用数组。但数组保证不了子对象的唯一性,所以才用了集合。而使用有序集合只是为了将来使用起来索引方便,而并不是排序。**作者下面也有提到:
> NSSet seems like an odd choice, doesn’t it? Unlike arrays, sets don’t allow accessing their members by index. In fact, there’s no ordering at all! Core Data uses NSSet because a set forces uniqueness in its members. The same object can’t feature more than once in a to-many relationship.
这里同样要注意的一点,还是创建完子类记得去model对应的Entity那里,Data Model inspector下填写相应的Class路径。
### **八、A walk down persistence lane**
接着就去ViewController做一些工作来使用CoreData,完成狗狗散步的App,这一步也比较简单。
### **九、Deleting objects from Core Data**
删除一条记录,先删除context中的相应object,保存后,从UI中删去相关条目。