StupidBeauty
Read times:875Posted at:Mon Oct 14 00:34:33 2013
- no title specified

iOS开发文档翻译:核心数据编程指南/使用一个被管理对象模型,Core Data Programming Guide/Using a Managed Object Model

使用一个被管理对象模型

此文章说明的是,如何在妳的程序中使用一个被管理对象模型

创建及载入一个被管理对象模型

通常,妳会按照 Core Data模型编辑器帮助 中的说明来使用Xcode 创建模型。妳也可以完全用代码来创建一个模型,在清单3中有示例,在Core Data工具教程中也有说明——然而,一般情况下,这么做太费劲,只能适用于最简单的程序。(当然,我们还是鼓励妳阅读一下这篇教程,以了解一下,建模工具都做了些什么,尤其是了解到这一点:模型只是由一组对象组成的一个集合。)

编译一个数据模型

一个数据模型是一个需要部署的资源。除了模型中各个实体和属性的详细信息之外,妳在Xcode 中创建的模型还包含了与图解(diagram)相关的信息—它的布局、颜色和元素等等东西。后面所说的这些信息在运行时是无用的。模型文件是使用模型编译器momc来编译的,它会删除无关的信息,使得在运行时尽可能快地载入该资源。一个xcdatamodeld“源代码”目录会被编译为一个momd 部署目录,而一个xcdatamodel“源代码”文件会被编译为一个mom 部署文件。

momc位于/Developer/usr/bin/。如果妳想在自己的构建脚本里使用这个工具,那么,它的用法是momc source destination,其中,source是要编译的Core Data 模型的路径,destination是输出的结果的路径。

载入一个数据模型

在某些情况下,妳不需要写任何用来载入模型的代码。如果妳在OS X平台上使用一个基于文档的程序,则NSPersistentDocument会为妳完成寻找及载入模型文件的任务。如果妳使用Xcode来创建一个非基于文档而又使用了Core Data 的程序(针对OS X或iOS),则程序本身的代表类中会包含有获取该模型的代码。一个模型的名字—由那个用来将它保存到硬盘上的文件的名字来表示——在运行时是不重要的。一旦Core Data 载入了该模型,它的文件名就没有意义了,也就没有用处了,所以,妳可以随意给该模型文件起名字。

如果妳想要亲自载入一个模型的话,可有两种方式:

  • •.妳可使用实例方法 initWithContentsOfURL: 来从一个指定的统一资源定位符(URL)处载入单个模型。

    这是常见的首选技巧。一般情况下一个程序只有一个模型,使用这个方法可以确保妳只载入了那个模型。妳还可以从多个统一资源定位符处载入多个模型,再在针对它们实例化一个协调器之前使用 modelByMergingModels: 来将它们合并起来。

    如果妳有多个模型—尤其是当这多个模型表示的是同一个模式(schema)的不同版本的时候——就有必要弄清楚到底要载入哪个模型了(在运行时,将拥有相同实体的多个模型合并到一起,会引起命名冲突和错误)。假如妳想要将模型文件保存在妳的程序包之外,因而需要通过一个文件系统的统一资源定位符来载入的话,也需要用上这个方法。

  • •.妳还可以使用类方法 mergedModelFromBundles: 来从一组指定的包(bundles)里创建一个合并后的模型。

    当模型的区分(segregation)不重要时,这个方法可能会有用处——例如,可能妳的程序和妳所链接到的某个框架都提供了一些模型,并且妳都需要载入或者想要载入它们。这个类方法使得妳可以轻易地一次性载入这些模型,而不用:考虑这些模型的名字;在特殊的初始化代码里确认这些模型都存在。

如果妳的项目中包含了多于一个模型则可能会出现问题

在某些场景下,当妳尝试着载入一个模型时,可能会出现问题。一般都是因为妳的项目中某些构建结果已经过时,而又同时使用了 mergedModelFromBundles: 这个类方法而引起的:

  • •.如果妳直接将模型文件重命名,则,Core Data会尝试着将现有版本与旧版本合并起来,此时会遇到类似以下内容的错误:

reason = "'Can't merge models with two different entities named 'EntityName''";

  • •.如果妳创建一个新的模型,其中含有与妳原来的模型中不同的实体,则,Core Data会合并旧的和新的模型。如果妳有一个已存在的存储,则当妳尝试着打开它时,会遇到类似下面内容的错误:

reason = "The model used to open the store is incompatible with the one used to create the store";

有两种解决方法:

  • •.在运行程序之前,确认一下妳已经将之前构建的旧结果都清除了。如果程序包本身包含有旧的模型文件,则删除程序本身。

  • •.不使用mergedModelFromBundles:,而使用initWithContentsOfURL:来初始化模型。统一资源定位符本身就能够唯一地标识一个模型,这样,Core Data就不会将当前模型与任何的旧模型合并了。

改变模式将会导致一个模型与旧的存储不兼容

因为一个模型描述的是某个持久化存储中的数据结构,所以,当妳改变了该模型中的任何部分而引起模式改变时,就会使得它与自己之前创建的存储不兼容(因而就无法打开那个存储了)。因此,如果妳改变了妳的模式,则,需要将已有存储中的数据迁移到新版本中去(参考 Core Data模型版本控制和数据迁移编程指南 )。例如,如果妳添加了一个新的实体或者向某个已有实体添加了一个新的值属性,则,妳无法再打开旧的存储了;如果妳添加了一个有效性限制条件或者给某个值属性设置了一个新的默认值,则,妳还可以打开旧的存储。

重要:如果妳想要在改变模型的同时还保持对使用旧版本的模型创建的存储的打开能力,则,妳必须保留之前旧版本的模型(作为一个带版本的模型中的某个版本)。Core Data无法打开一个没有与其相兼容的模型的存储。所以,如果妳想在改变模型的同时还保持打开旧存储的能力,则必须:

  1. 1.确保妳拥有一个带版本的模型——如果没有,则将当前模型变成带版本的模型。

  2. 2. 在编辑模式之前为当前模型创建一个新版本

  3. 3.编辑此模型的新的当前版本,而不要改动旧版本。

在运行时访问及使用一个被管理对象模型

某些情况下,妳需要在运行时访问到模型,一般是为了——例如——获取一个取信息请求模板(fetch request template)、一个本地化的实体名字或者是某个值属性(attribute)的数据类型。妳甚至还可能需要用代码来修改该模型(在运行时,妳只能在它被使用之前做这件事,参考 NSManagedObjectModel )。有多种方法可以让妳在运行时访问到一个被管理对象模型。妳将会通过持久化栈(persistence stack)中的持久化存储协调器(persistent store coordinator)来最终获取这个模型。所以,可以使用以下代码来从一个被管理对象上下文(managed object context)中获取到模型:

[[<#某个被管理对象上下文#> persistentStoreCoordinator] managedObjectModel];

妳还可以从一个实体描述中获取到模型,所以,给定了一个被管理对象之后,妳就可以获取到它的实体描述,进而获取到模型了,就像以下代码这样。

[[<#某个被管理对象#> entity] managedObjectModel];

在某些情况下,妳会保留对模型的“直接”引用—即,拥有某个直接返回该模型的方法。NSPersistentDocument提供了managedObjectModel 这个方法,它返回的是由此文档的被管理对象上下文所使用的持久化存储协调器所关联到的那个模型。如果妳使用Core Data应用程序(Core Data Application)模板来创建妳的项目,则,应用程序本身的代表(delegate)会保留对模型的引用。

使用代码来创建取信息请求模板

妳可以使用代码来创建取信息请求模板,再使用 setFetchRequestTemplate:forName: 来将它们与一个模型关联起来,见清单1。但是,再次说明,妳只能在模型被某个存储协调器使用之前修改它。

清单 1 使用代码来创建一个取信息请求模板

NSManagedObjectModel *model = <#获取一个模型#>;

NSFetchRequest *requestTemplate = [[NSFetchRequest alloc] init];

NSEntityDescription *publicationEntity =

[[model entitiesByName] objectForKey:@"Publication"];

[requestTemplate setEntity:publicationEntity];

NSPredicate *predicateTemplate = [NSPredicate predicateWithFormat:

@"(mainAuthor.firstName like[cd] $FIRST_NAME) AND \

(mainAuthor.lastName like[cd] $LAST_NAME) AND \

(publicationDate > $DATE)"];

[requestTemplate setPredicate:predicateTemplate];

[model setFetchRequestTemplate:requestTemplate

forName:@"PublicationsForAuthorSinceDate"];

访问取信息请求模板

妳可以按照“在运行时访问及使用一个被管理对象模型”中代码片断演示的方式来获取并使用一个取信息请求模板。置换字典(substitution dictionary)中必须包含此模板中定义的所有变量的键;如果妳想测试一个空值,则必须使用一个NSNull对象——参考“使用断言”

清单 2 使用一个取信息请求模板

NSManagedObjectModel *model = <#获取一个模型#>;

NSError *error = nil;

NSDictionary *substitutionDictionary = [NSDictionary dictionaryWithObjectsAndKeys:

@"Fiona", @"FIRST_NAME", @"Verde", @"LAST_NAME",

[NSDate dateWithTimeIntervalSinceNow:-31356000], @"DATE", nil];

NSFetchRequest *fetchRequest =

[model fetchRequestFromTemplateWithName:@"PublicationsForAuthorSinceDate"

substitutionVariables:substitutionDictionary];

NSArray *results =

[aManagedObjectContext executeFetchRequest:fetchRequest error:&error];

如果此模板中没有置换(substitution)变量,则,妳必须做以下两件事之一:

  1. 1.使用fetchRequestFromTemplateWithName:substitutionVariables:,并且将nil 作为变量(variables)参数传递;或者

  2. 2.使用fetchRequestTemplateForName:,然后复制copy)其结果。

    如果妳试图直接使用fetchRequestTemplateForName:所返回的取信息请求的话,会产生一个异常(“无法在一个不可变的模型中修改一个命名的取信息请求”("Can't modify a named fetch request in an immutable model"))。

将一个被管理对象模型本地化

妳可以将一个被管理对象模型的大部分信息本地化,包括实体和属性名字以及错误消息。有一点重要的事需要注意,那就是,本地化还意味着“以妳自己的语言来说明问题”("localization into your own language")。即使妳不准备为妳的程序提供国外的语言版本,妳也可以通过这一点来提供一个更好的用户体验:在错误消息中显示出“自然语言”风格的名字而不是“电脑语言”风格的名字(例如,“名字是一个必需属性”("First Name is a required property"),而不是“firstName是一个必需属性”("firstName is a required property"))。

妳可以通过提供一个本地化字典来将一个模型本地化,字典的模式在下表中展示。

表 1 针对一个被管理对象模型的本地化字典中的键和值

备注

"Entity/未本地化的实体名字"("Entity/NonLocalizedEntityName")

"本地化的实体名字"

"Property/未本地化的属性名字/Entity/EntityName"("Property/NonLocalizedPropertyName/Entity/EntityName")

"本地化的属性名字"

1

"Property/未本地化的属性名字"("Property/NonLocalizedPropertyName")

"本地化的属性名字"

"ErrorString/未本地化的错误信息字符串"("ErrorString/NonLocalizedErrorString")

"本地化的错误信息字符串"

备注:(1) 针对的是,在不同的实体中,拥有相同的未本地化名字,但是却应当翻译成不同的本地化名字的属性。

妳可以使用 localizationDictionary 方法来访问到本地化字典。然而,要注意,在OS X 版本10.4 中,localizationDictionary可能会返回nil,直到Core Data因为自身的需求(例如,报告一个本地化的错误消息)而对这个字典完成懒加载之后才会返回正常内容。

字符串文件

用来本地化一个模型的最简单方式就是创建一个对应的字符串文件——这个字符串文件的名字与模型文件的名字一致,但是扩展名是.strings,而不是.xcdatamodel(例如,对于一个名叫MyDocument.xcdatamodel的模型文件,它对应的字符串文件名叫MyDocumentModel.strings—如果妳的模型文件名中已经有一个后缀"Model"了,则,妳必须在字符串文件名中再额外追加一个"Model",所以 ,对应于JimsModel.xcdatamodel 的字符串文件名应当是看起来有点2的 JimsModelModel.strings)。这个文件的格式与那些用来作本地化用途的标准字符串文件(参考“本地化字符串资源”)的格式类似,但是其键与值的模式要遵守表1

对于某个拥有一个雇员实体的模型,其字符串文件将是类似于这样的:

"Entity/Emp" = "Employee";

"Property/firstName" = "First Name";

"Property/lastName" = "Last Name";

"Property/salary" = "Salary";

使用代码来设置一个本地化字典

妳可以在运行时使用NSManagedObjectModel setLocalizationDictionary:方法来设置一个本地化字典。妳必须按照表1中的说明来创建一个字典,然后将它与模型关联起来。妳必须确保在该模型被用于获取或创建被管理对象之前就做这件事,因为之后这个模型将会变成只读的。清单3展示了创建一个带本地化字典的被管理对象模型的代码。该实体名为“运行”("Run"),在运行时由Run 类来表示。这个实体拥有两个值属性(attributes),“日期”("date")和“进程编号”("processID")——分别是一个日期和一个整数。进程编号(process ID)拥有一个限定条件,它的值不能小于0.

清单 3 使用代码来创建一个被管理对象模型

NSManagedObjectModel *mom = [[NSManagedObjectModel alloc] init];

NSEntityDescription *runEntity = [[NSEntityDescription alloc] init];

[runEntity setName:@"Run"];

[runEntity setManagedObjectClassName:@"Run"];

[mom setEntities:@[runEntity]];

NSMutableArray *runProperties = [NSMutableArray array];

NSAttributeDescription *dateAttribute = [[NSAttributeDescription alloc] init];

[runProperties addObject:dateAttribute];

[dateAttribute setName:@"date"];

[dateAttribute setAttributeType:NSDateAttributeType];

[dateAttribute setOptional:NO];

NSAttributeDescription *idAttribute= [[NSAttributeDescription alloc] init];

[runProperties addObject:idAttribute];

[idAttribute setName:@"processID"];

[idAttribute setAttributeType:NSInteger32AttributeType];

[idAttribute setOptional:NO];

[idAttribute setDefaultValue:@0];

NSPredicate *validationPredicate = [NSPredicate predicateWithFormat:@"SELF >= 0"];

NSString *validationWarning = @"Process ID < 0";

[idAttribute setValidationPredicates:@[validationPredicate]

withValidationWarnings:@[validationWarning]];

[runEntity setProperties:runProperties];

NSDictionary *localizationDictionary = @{

@"Property/processID/Entity/Run" : @"进程编号",

@"Property/date/Entity/Run" : @"日期"

@"ErrorString/Process ID < 0" : @"进程编号不同小于0" };

[mom setLocalizationDictionary:localizationDictionary];

刘云

未知美人

未知美人

Your opinions
Your name:Email:Website url:Opinion content:
- no title specified

HxLauncher: Launch Android applications by voice commands

 
Recent comments
2017年4月~2019年4月垃圾短信排行榜Posted at:Thu Sep 26 04:51:48 2024
Qt5.7文档翻译:QWebEngineCookieStore类,QWebEngineCookieStore ClassPosted at:Fri Aug 11 06:50:35 2023盲盒kill -9 18289 Grebe.20230517.211749.552.mp4