Core Data Programming Guid

(转)

关于Persistent Stack

对象和外部数据存储,这两者之间的媒介,被整体叫做persistence stack。其中,managed object context位于栈顶,persistent object store位于栈底,中间的是persistent store coordinator。

Persistent stack

实际上,是persistent store coordinator决定着这个栈。它使用了facade模式,使得栈底的多个persistent store,在呈现给context的时候,就像一个整体一样。
一个coordinator只能和一个managed object model相关联。


关于Managed Object Model

一个managed object model是NSManagedObjectModel类的实例。它描述了第三方app中需要使用到的一系列entity,和多个entity之间的关系。
一个model中可能有很多NSEntityDescription对象来代表这个model的各个entity。对于每个entity来说,有两个很重要的特性,一个是这个entity的名字,另一个是在运行时,表示这个entity的类的名字

一个entity可能会有attribute、relationship,也可能有fetched property,这三者统称为property。需要注意的是,property不能和NSObject或NSManagedObject已有的方法名重叠,比如,不能给某个property起名为“description”。
比较特殊的一种property叫做transient property,它是不会被保存到persistent store中去的。

多个entity之间可能会有继承关系,也可能某个entity会被指定为抽象的。

大多数model中的元素(比如entity、attribute、relationship)都会有一个对应的user info。


创建一个model

使用Xcode创建model

在Xcode中,选择File->New->File->Core Data->Data Model就可以创建一个扩展名为.xcdatamodeld的“源文件”了(实际上应该是一个目录)。其中包含了一个扩展名为.xcdatamodel的“源文件”。可以使用Xcode的Core Data model editor,在xcdatamodel文件中编辑model的内容,比如其中包含什么样的entity,每个entity中有什么样的attribute,以及各个entity之间的关系,等等。

如果App更新时,需要对model进行改动,就需要创建一个新的model version。在Xcode中,选中xcdatamodeld,选择Editor->Add Model Version,可以继续创建其中的xcdatamodel“源文件”。

除了model中关于entity和property的各种信息,xcdatamodel还会包含一些其他信息,比如绘制的图表的宽高排列之类的,但这些信息在运行时并没有什么意义。所以,model文件的编译工具momc会把运行时没有意义的信息去掉,将xcdatamodel文件编译成mom文件,将xcdatamodeld目录编译成momd目录。

在Xcode中找到编译好的.app文件,右键Show in Finder,打开里面的内容后,可以看到其中的.momd文件夹,和这个文件夹里面的.mom文件。

如果写的是iOS上的app,则在需要程序员自己加载model文件。有这样两种方法:

  1. 使用NSManagedObjectModel的initWithContentOfURL:方法。
    这是一种比较普遍使用的方法。

    NSURL *modelURL = [[NSBundle mainBundle] URLForResource:modelName withExtension:@"momd"];
    NSManagedObjectModel *model = [[NSManagedObjectModel alloc] initWithContentsOfURL:modelURL];
  2. 使用mergedModelFromBundles:方法.
    如果参数是nil,则会搜索main bundle,把其中的所有model给merge起来。
在代码中创建\修改model

在model被一个managed object context或者一个persistent store coordinator使用之前,这个model是可以在代码中被修改的。这允许程序员动态的创建或修改model。

试了一下在代码中创建model:

NSManagedObjectModel *model = [[NSManagedObjectModel alloc] init];
NSEntityDescription *launchInfoEntity = [[NSEntityDescription alloc] init];
[launchInfoEntity setName:@"LaunchInfo"];

NSAttributeDescription *dateAttribute = [[NSAttributeDescription alloc] init];
[dateAttribute setName:@"date"];
[dateAttribute setAttributeType:NSDateAttributeType];
[dateAttribute setOptional:NO];

[launchInfoEntity setProperties:@[dateAttribute]];

[model setEntities:@[launchInfoEntity]];

如果model是在被一个managed object context或者一个persistent store coordinator使用之后,受到改动,则会抛出exception:

*** Terminating app due to uncaught exception ‘NSInternalInconsistencyException‘, reason: ‘Can‘t modify an immutable model.‘

Fetch Request Template

程序员可以使用NSFetchRequest类来描述从持久化存储中取得一些对象的请求。在实际的开发中,同样或相似的请求往往会被执行多次,所以,程序员可以自定义一些fetch request template,并把它们存到model中。可以使用Xcode的Core Data model editor,也可以在代码中定义。

使用Core Date model editor定义fetch request template

Editor->Add FetchRequest来新建一个fetch request。

填写Predicate,可以使用变量。右边栏还可以指定一些高级选项。

指定Predicate

在需要使用时,只要在代码中取出对应的fetch request template:

NSManagedObjectModel *managedObjectModel = [[context persistentStoreCoordinator] managedObjectModel];
NSFetchRequest *fetchRequest = [managedObjectModel fetchRequestFromTemplateWithName:@"fetchLaunchInfoBeforeSomeDate"
                                                              substitutionVariables:@{@"DATE" : [NSDate date]}];
NSArray *fetchResult = [context executeFetchRequest:fetchRequest error:&error];

就可以正常使用了。

直接在代码中创建fetch request template

也可以完全动态的创建fetch request template:

NSManagedObjectModel *managedObjectModel = [[context persistentStoreCoordinator] managedObjectModel];
NSFetchRequest *fetchRequestTemplate = [[NSFetchRequest alloc] initWithEntityName:@"LaunchInfo"];
[fetchRequestTemplate setPredicate:[NSPredicate predicateWithFormat:@"date > $DATE"]];
[managedObjectModel setFetchRequestTemplate:fetchRequestTemplate forName:@"fetchLaunchInfoAfterSomeDate"];

关于Configuration

如果程序员想要把不同的entity存放到不同的persistent store中去,应该怎么做呢?一个coordinator只能对应一个managed object model,所以在默认情况下,每一个与这个coordinator相关联的persistent store,都存放了同样的entity。为了避免这样的限制,可以使用Configuration来指定每个persistent store中应该存放哪些entity。
指定了Configuration之后,当程序员取这些对象的时候,它们会自动从不同的文件中被取出;保存时,它们也会被自动保存到不同的文件。

一个configuration由名字和若干entity组成。可以在代码中用

setEntities:forConfiguration:

方法动态的定义configuration;

也可以在Core Data editor tool中定义:

指定Configuration

每当给coordinator增加persistent store的时候,只用在configuration参数中指定对应的configuration即可以使用:

if (![coordinator addPersistentStoreWithType:NSSQLiteStoreType
                               configuration:@"ExitInfoConfiguration"
                                         URL:exitInfoStoreURL
                                     options:nil
                                       error:&error]) {
    //Handle error
}

if (![coordinator addPersistentStoreWithType:NSSQLiteStoreType
                               configuration:@"LaunchInfoConfiguration"
                                         URL:launchInfoStoreURL
                                     options:nil
                                       error:&error]) {
    //Handle error
}

关于Managed Object

一个managed object代表的是一个entity的实例。

每个managed object与一个managed object context相关联。在一个特定的context中,持久化存储中的一个特定的记录,只能有一个对应的managed object,这种技术叫做Uniquing。但是,也可能有多个context,每个context都持有一个表示同一条记录的managed object。


关于accessor方法

可以使用Xcode根据xcdatamodel中的内容自动生成NSManagedObject的子类。在子类的实现中,我们能看到,property被@dynamic修饰了。那是因为Core Data会在运行时动态生成accessor方法,这样生成的accessor方法是比较高效的,也就是说,程序员一般不需要写自定义的accessor方法。

也可以通过key-value的形式来获取或设置attributes的值,但是在性能上KVC不如accessor方法,所以只应该在必要的情况下使用。

如果这个managed object有to-many relationship,很多时候,程序员可能会需要增添、删除或改动这个to-many relationship中的某几个元素,这个时候则应该使用mutableSetValueForKey:方法或者动态生成的relationship mutator方法。


关于Managed Object的生命周期

一个managed object的生命周期和标准的Cocoa对象的生命周期不太一样,因为那是由Core Data来管理的。一个managed object表示的数据的生命周期,和这个manged object的实例的生命周期是独立的。

可以通过一个managed object得到它所在的context,也可以通过一个context得到其中的managed object。但是默认情况下,managed object和context之间的引用是弱引用。然而有一种例外情况,context会对“被改动过的”managed object持强引用,这里的改动包括插入、删除和修改,直到context被save、reset或者rollback。同时,undo manager也会用强引用来维持被改动过的managed object。
可以用setRetainsRegisteredObjects:方法改变这种默认情况,使得context对managed object持强引用。

当managed object有relationship的时候,它会对这个关联的对象持强引用,这也意味着可能有强引用循环出现。所以,当使用完一个managed object的时候,应该用refreshObject:mergeChanges:方法让它成为一个fault。

在一个managed object被创建的时候,其中每个property的值是在对应的entity中的default value。如果需要做一些自定义的初始化,建议重写:awakeFromInsert或者awakeFromFetch方法。

其中,awakeFromInsert会在调用了initWithEntity:insertIntoManagedObjectContext:或者insertNewObjectForEntityForName:inManagedObjectContext:方法之后立刻被调用。所以,重写这个方法,主要是可以为managed object中的property提供特殊的默认值,比如这个对象被创建的时间。

awakeFromFetch方法会在managed object从一个持久化存储中被取出来的时候调用。重写这个方法,可以用于建立transient值和缓存。需要注意的是,如果在这个方法中,改变了managed object中某些property,context不会被认为是dirty的。这也就意味着不应该在这个方法中操纵relationship,因为目标对象不会为此做出应有的改变。

initWithEntity:insertIntoManagedObjectContext: 这个方法也可以重写,但是并不鼓励这样做。因为在重写的这个方法中改变的状态,可能会不支持undo和redo。

在需要“析构”的时候,不应该重写dealloc方法,而是应该重写didTurnInfoFault方法。这个方法会在managed object变成fault的时候被调用,也就是说会比真正的析构早一些。


关于Relationship

大多数的relationship天生就是双向的(一个主要的例外就是fetched property)。一般来说,在使用Core Data的时候,也应该为relationship指定反向关系,这样可以确保object graph的一致性。

一个relationship是有delete rule的。这指定了当这个对象即将被删除的时候应该发生的行为。有这样几种delete rule:

  1. Deny
    如果至少有一个relationship的目的对象存在,源对象是不能被删除的;
  2. Nullify
    在删除当前对象的同时,将relationship的目的对象的反向关系设置为null;
  3. Cascade
    在删除当前对象的同时,也删除relationship的目的对象;
  4. No Action
    在删除当前对象的同时,对relationship的目的对象不做任何操作。在使用这个delete rule的时候,程序员有责任自行维护object graph,所以应该将对应的反向关系设置成有意义的值。

关于Object ID

一个NSManagedObjectID对象是managed object的全局ID。Object ID有临时和持久之分。当一个managed object刚刚被创建时,它将获得一个临时的object ID;只有当它被保存到持久化存储中时,它才会被赋予一个持久的ID。

Object ID也可以被转化成URI。可以使用 managedObjectIDForURIRepresentation:方法或objectWithID:方法通过URI或ID获取对应的managed object。


关于Validation

Validation机制用于检验managed object的property的值是否满足一定条件。有两种validation的类型,分别是:

  1. property层次的validation
  2. property之间的validation

Core Data允许程序员在managed object model中设定简单的validation逻辑。比如,可以设置数字和日期的最大最小值,可以设置字符串的最大最小长度、需要匹配的正则表达式,还可以设置to-many relationship中数目的最大最小值。

在Core Data Model editor中可以设置一些validation逻辑

除了可以对model设置这些validation逻辑,还可以在代码中进行自定义。

如果想要自定义property层次的validation,程序员不应该重写validateValue:forKey:error:方法,而是应该实现validate<Key>:error:方法。
然而,如果想要自行检查某个property是否符合规定,应该调用的是validateValue:forKey:error:方法,这个方法会将定义在managed object model中的validation逻辑也考虑进去。

也可以自定义property之间的validation。这可以通过重写validateForUpdate:validateForInsert:validateForDelete:方法来实现。在重写的这三个方法中,应该首先调用父类的实现。

所有的validation限制都只有在保存操作的过程中会被应用。因为managed object context的本意就是一块草稿板,所以应该允许其中的对象有临时性的“不合理”。


关于Faulting

一个managed object通常会用于表示被持久化存储的数据,但是在有些情况下,一个managed object可能是fault的,也就是说它的property还没有从外部数据存储中载入进来。这是Core Data用于减少内存占用的一种机制。

当访问到一个managed object的某个持久化的property的时候,fault被触发了,如果内存中的cache没有被击中的话,数据会被自动从持久化存储中取过来,这里的开销是比较昂贵的。

需要注意的是,description方法是不会触发fault的,所以打印刚刚取出来的managed object可以看到“<fault>”字样。
比如这样:

"<LaunchInfo: 0x10060b450> (entity: LaunchInfo; id: 0x40000b <x-coredata://4973AB39-0CD8-4480-AA07-7A3A877BE87D/LaunchInfo/p1> ; data: <fault>)"

如果重写description方法,并在其中访问了某个持久化的property,则fault会被触发。所以应该尽量避免这样的做法。

可以使用refreshObject:mergeChanges:并传人参数no让一个managed object变成fault。但是必须保证其中的relationship没有被改变。


关于Fetching

取得指定的对象

如果app使用了多个context,那么程序员可能就需要测试一个对象是否已经从persistent store中被删除了。这时,可以创建一个fetch request,其中这样指定predicate:

NSPredicate *predicate = [NSPredicate predicateWithFormat:@"self == %@", targetObject];

这样就可以通过判断fetch到的对象的数目是否为0来判断目标对象是否已被删除。其中的targetObject可以是一个managed object,也可以是一个manged object ID。
如果一次需要测试多个目标对象是否被删除,可以使用更高效的IN操作符:

NSPredicate *predicate = [NSPredicate predicateWithFormat:@"self IN %@", arrayOfManagedObjectIDs];
获取特定的值

有的时候,程序员可能不需要获取整个managed object,而是只是需要其中的某个attribute。NSExpressionDescription可以帮助程序员取得需要的值。
这时,需要使用setResultType:方法来指定这个fetch返回的结果类型是NSDictionaryResultType;还需要创建NSExpressionDescription的实例,来指定哪些property是需要取得的。
官方文档里有示例代码,偷个懒。


还欠缺的部分

这篇博客真是拖着写了好久。
但是还有好多内容没有理解,因为偷懒+之前在工作中对这些部分接触不多没什么感受,所以先放在这里,等下一遍看的时候,再慢慢理解好了。

Localizing a Managed Object Model
Copying and Copy and Paste
Drag and Drop
Undo Management
Ensuring Data Is Up-to-Date
Change and Undo Management
Fetched Properties
Non-Standard Persistent Attributes
Associate Metadata With a Store to Provide Additional Information and Support Spotlight Indexing
Core Data and Cocoa Bindings
Change Management
Persistent Store Features
Core Data Performance
Troubleshooting Core Data
Efficiently Importing Data

时间: 2024-10-14 19:26:22

Core Data Programming Guid的相关文章

Core Bluetooth Programming Guide - Performing Common Peripheral Role Tasks 粗译

Core Bluetooth Programming Guide 粗译 续 code {padding:0.2em 0.4em; background:#e1e9ed;} pre {text-align:left; overflow-x: scroll; color: #d3bd7e; background: #202020; padding: 10pt 15pt; border-radius: 3px;} hr {border: 0px; border-top: 1px solid #ddd;

Core Data Memory Management - Reducing Memory Overhead

使用Core Data 中经常遇到的一个很重要的问题就是内存问题,因此需要很谨慎地考虑内存问题,否则在工程规模比较大或者操作大量数据的时候就显得尤为重要,下面是Apple官方文档的说明. OverView It is sometimes the case that you want to use managed objects on a temporary basis, for example to calculate an average value for a particular attr

Core Data 版本迁移经验总结

大家在学习和使用Core Data过程中,第一次进行版本迁移的经历一定是记忆犹新,至少我是这样的,XD.弄的不好,就会搞出一些由于迁移过程中数据模型出错导致的Crash.这里总结了一下Core Data版本迁移过程中的经验,希望对大家有用. 写在前面 关于Core Data版本迁移,这两篇文章都进行了分析,大家可以参考. Core Data Model Versioning and Data Migration Programming Guide 自定义 Core Data 迁移 迁移准备 1)

核心动画编程指南Core Animation Programming Guide - iOS

1 有关核心动画 1.1 概览 1.1.1 Core Animation 管理应用的内容 核心是 layer objects, 1.1.2 更改 layer 触发动画 Like views, layer objects have a bounds rectangle, a position onscreen, an opacity, a transform, and many other visually-oriented properties that can be modified. 更改这

ios开发学习笔记--数据持久化之Core Data

数据持久化之CoreDate 一.     Core Data简介 Core Data是苹果公司为了实现数据持久化提供的一个框架,而不是一个数据库,提供对象-关系映射[ORM]的功能(将对象转化为数据,也能将保存在数据库的数据还原为对象). 虽然底层操作与SQLite相似,但是并不直接写SQL语句,而且只能整个取出实体记录,然后分解之后才能得到某个属性. Core Data能存储的类型:SQLite数据库.XML.二进制.自定义数据类型 一般情况下,Core Data的数据存在沙盒下的Docum

Core Data

简介 Core Data是iOS5之后才出现的一个框架,它提供了对象-关系映射(ORM)的功能,即能够将OC对象转化成数据,保存在SQLite数据库文件中,也能够将保存在数据库中的数据还原成OC对象.在此数据操作期间,我们不需要编写任何SQL语句,这个有点类似于著名的Hibernate持久化框架,不过功能肯定是没有Hibernate强大的.简单地用下图描述下它的作用: 左边是关系模型,即数据库,数据库里面有张person表,person表里面有id.name.age三个字段,而且有2条记录: 右

我为什么用 SQLite 和 FMDB 而不用 Core Data

转:http://segmentfault.com/a/1190000000363392 编者注:文章的"我"是指原作者. 凭良心讲,我不能告诉你不去使用Core Data.它不错,而且也在变好,并且它被很多其他Cocoa开发者所理解,当有新人加入你的组或者需要别人接手你的项目的时候,这点很重要.更重要的是,不值得花时间和精力去写自己的系统去代替它.真的,使用Core Data吧. 为什么我不使用Core Data Mike Ash写到: 就我自己而言,我不是个狂热粉丝.我发现API是

Core Data存储自定义类型数据

目录: 一.使用CoreData存储基本数据 二.使用CoreData存储自定义类型数据 简单介绍CoreData CoreData是iOS编程中使用持久化数据存储的一种方式,我们知道CoreData并不是数据库本身,而是Apple提供的对象持久化技术--Object Persistent technology.CoreData框架为我们的数据变更.管理.对象存储.读取和恢复提供了支持.下面我们来尝试创建一个简单的CoreData Project. 操作 1. 打开x-code,为你的proje

Core Data使用之一(Swift): 保存

Core Data 用于永久化数据,它是基于SQLite数据库的保存一门技术. 那么,在Swift中,它是如何实现的呢? 首先,需要新建一个模板,打开工程中的xcdatamodeld文件,点击“Add Entity” ,这时候,就创建的一个模板.之后,可以修改模板的名称为自己想要的名称.然后,在Attributes里面,点击“+”,添加字段并修改类型. 然后,在代码里面 “import CoreData”.接着,用NSManagedObject来保存对象,它可以转换成任何对象.它的类型是字典.