第十一章: Core Data基本原理
到现在,我们实现的程序可以维护一个对象链,实现Undo功能,还可以save和load文件. 你可以想象一下,应该有很多的程序需要完成类似的功能
Apple希望通过一些方法可以让这类程序更容易开发
. NSArrayController保持维护一个对象链
. 绑定机制避免了编写很多model对象和view对象同步更新的所需代码
. NSManagedObjectContext类将会监测model对象的成员变量,处理undo,save和load功能
使用Core Data和绑定, 我们可以不编写任何代码来创建类似RaiseMan这样到程序. 在这章中,我们就会来创建一个简单的Core Data程序, 和RaiseMan不一样,我们不会编写任何代码
--- NSManagedObjectModel ---
系统为了知道怎么来save和load对象中的数据,它需要知道这些数据是什么样的:对象的属性名字是什么?它们的类型是什么?为了提供这样的信息,我们需要生成model. XCode有一个编辑器让我们可以很简单的来生成这样的model. 在运行的时候,程序会读取model文件来生成NSManagedObjectModel对象
model使用了一些不太一样的名字术语. 对于class,model称之为 entity; 对于instant variable, model 称之为property
model包含两种property: attributes 和 relationships. attribute为简单数据类型,如一个字符串,日期,数字. relationship我们会在后面介绍
在RaiseMan中,我们使用了NSDocument的子类MyDocument. 而在这个程序中,MyDocument的父类是NSPersistentDocument. NSPersistentDocument类会自动读取model文件并生成一个NSManagedObjectContext对象. NSPersistentDocument类内部包含了大量我们所需的代码[不要自己编写咯,甚至不需要自己直接去调用,系统帮你做了]
打开XCode,创建一个新的工程-Core Data Document-based Application . 命名为CarLot. 假设你有一个汽车销售店, 这个程序就是用来帮助你管理你要出售的汽车. 它会是这样的:如图11.1
回到XCode, 在工程的Models下打开 MyDocument.xcdatamodel . 找到Entity table view. 点击+ 来添加一个新的entity, 并命名为Car
选中entity Car, 在Properties table view下面有一个弹出按钮,点击并选择Add Attribute,依照下表添加六个attributy
图11.2 展示了添加后的样子
虽然我们还可以添加更多的其他东西到model中, 不过对于这个练习现在已经够用了
--- Interface ---
打开Mydocuent.nib , 从Window中删除 your document contents here.拖动一个array controller到doc窗口. 这个array controller会使用document对象的NSManaedObjectContext对象来获取和保存数据. 使用Bindings Inspector将array controller的managedObjectContext 和 File's Owner 的managedObjectContext绑定如图11.3
在Attributes Inspector中,设置让array controller从Car entity中获取数据,如图11.4 . 同时勾选Prepares Content, 使的array controller在创建后立马从model中获取数据(doc window中的对象下面的标签可以设置为任何名字, 在这个例子中我将 array controller 标签改成 Cars. 这样当有多个array controller存在时不会搞混了)
--- 创建,配置View ---
拖拽一个table view到窗口(从 Cocoa->Views & Cells->Data Views). 在Attributes Inspector中设置为3列,列名为Make/Model , Price,和Special, 再拖拽一个number formatter(from Cocoa->Views & Cells-> Formatters)到Price 列.选中这个fommatter(就在这一列的边上一个小圆形), 设置它显示货币. 使用10.4+格式,设置风格为Currency. 同时勾选Generate Decimal Numbers 和 Always Shows Decimal 如图10.5
第3列将显示为一个check box. 拖拽一个check box(从Cocoa->Views & Cells -> Cells)到第3列. 选中该cell,清空它的标题 如图11.6
在table view的下面,我们还需要一个NSDataPicker,两个按钮,一个NSImageView,和一个NSLevelIndicator. 给NSDataPicker和NSLevelIndicator添加两个text field. 按钮的标题修改为New 和 Delete. text field标题为Data Purchased: 和 Condition:. 在NSLevelIndicator的Attributes Inspector中,设置min为0,max为5 ,并设置风格为Rating模式(会显示为五角星).并勾选editable 如图11.7
设值NSImageView为editable. 同时选择date picker , image view 和连个 labels , level indicator. 点击 Layout->Embed Objects In -> Box 菜单.把它们都放置到一个box中去 如图11.8
>连接和绑定
接下来我们要完成一系列的绑定, 我们会一步一步来. 图11.9是我们将要完成的连接view和array controller的绑定
有一点要注意, 我们从来不会绑定到scroll view, table view 或是cell. 我们只会绑定到table view的某列. 列包含了cell,属于table view. 而table view是在scroll view里面
对每一列的value (Cars 就是NSArrayController)
使New 按钮激活arrayController的方法add: 图11.10
同样使Delete按钮激活array controller 的remove:方法
绑定下面控件的value到array controller
绑定image view的Data(不是Value噢)到Cars. controller key为selection,keypath为photo. 同时勾选Conditionally sets Editable 如图11.11
绑定box的Title With Pattern到Cars, Controller Key为selection. Model Key Path为makeModel . 设置Display Pattern为 Details for %{title}@ . 设置No Selection Placeholder为<No selection>. 设置Null Placeholder 为<no Make/Model>. 如图11.12 [想一下这些设定的会导致的cocoa内部工作流程是怎么样的]
让我们再来是实现如果car是special[check box为勾选]时,前两列的文字为粗体. 绑定Font Bold 到Cars's arrangedObjects onSpecial 如图11.13
完成了, 编译运行程序. Save和Load功能有了, Undo功能也有了.是不是很神奇呢?
-- Core Data 是怎么工作的 --
虽然我们没有编写任何代码, 不过Cocoa 内部还是自动为我们创建了很多的对象来完成程序的功能. 图11.14 为对象关系图
NSPersistentDocument读取创建好得data model来生成一个NSManagedObjectModel对象. 在我们的例子里, managed object model有一个NSEntityDescription来描述我们的Car entity. entity description包含了多个NSAttributeDescription对象
接下来,NSPersistentDocument生成一个NSPersistentStoreCoordin
因此,在Core Data 的类当中, 你会发现你会经常和NSManaedObjectContext进行交互. 使用它来获取对象,保存对象的改变等等
我们可以在程序中添加卖掉的car, 如果使得添加car的datePurchased属性为当天就更好了. 为了实现这个功能,一个好得方法就是继承NSArrayController类, 重载newObject方法
回到XCode, 创建一个Objective-C类文件. 命名为CarArrayController. 在.h中修改:
#import <Cocoa/Cocoa.h>
@interface CarArrayController : NSArrayController
{}
@end
在.m中,重载方法newObject
- (id)newObject
{
}
然后,在Interface Builder中, 将array controller的class属性修改为CarArrayCtroller 如图11.15
编译运行程序, 当添加一个Car时, datePurchased属性会初始的为当天了.