企业🤖AI智能体构建引擎,智能编排和调试,一键部署,支持私有化部署方案 广告
> 原文出处: 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中删去相关条目。