CoreData完整使用教程

What Is Core Data?


Core Data is a framework that you use to manage the model layer objects in your application. It provides generalized and automated solutions to common tasks associated with object life cycle and object graph management, including persistence.

 CD是一个在你的应用中你用来去管理模型层对象框架。它提供产生和使用对象生命周期和图表管理自动解决常见任务,包括持久化。


Core Data typically decreases by 50 to 70 percent the amount of code you write to support the model layer. This is primarily due to the following built-in features that you do not have to implement, test, or optimize:

Core Data减少百分之50到百分子70的你用来支持模型层的代码。这是因为一下的特征植入 你不用去实现测试或优化:



Change tracking and built-in management of undo and redo beyond basic text editing. 

改变追踪和超越基本文本编辑的撤销和重做的内置管理。


Maintenance of change propagation, including maintaining the consistency of relationships among objects. 

维护改变传播,包括保持对象间关系的一致性。


Lazy loading of objects, partially materialized futures (faulting), and copy-on-write data sharing to reduce overhead. 

对象的懒加载,部分物化特征(断层),和复制写入数据共享去减少开销。


Automatic validation of property values. Managed objects extend the standard key-value coding validation methods to ensure that individual values lie within acceptable ranges, so that combinations of values make sense. 

自动验证属性的值。管理对象延伸标准key-value编码验证方法去保证不可用的值分在可接受的范围,来让值的组合有意义。


Schema migration tools that simplify schema changes and allow you to perform efficient in-place schema migration. 

图表迁移工具简化图表的改变让你在图表迁移中执行高效。

FCS16072621464942

Optional integration with the application’s controller layer to support user interface synchronization. 

整合了应用的控制器层来支持用户界面的同步。


Grouping, filtering, and organizing data in memory and in the user interface. 

分组,过滤,组织数据 在内存中和在用户界面中。

Automatic support for storing objects in external data repositories. 

Sophisticated query compilation. Instead of writing SQL, you can create complex queries by associating an NSPredicate object with a fetch request. 

自动支持存储对象到外部数据库。复杂查询编译。而不是写一个SQL,你能造一个复杂查询通过一个使用了获取请求的谓词对象。


Version tracking and optimistic locking to support automatic multiwriter conflict resolution. 

版本追中和加锁去支持自动多重写入冲突解决。


Effective integration with the OS X and iOS tool chains. 

































Creating a Managed Object Model

造一个管理对象模型

Much of Core Data’s functionality depends on the schema you create to describe your application’s entities, their properties, and the relationships between them. Core Data uses a schema called a managed object model — an instance of NSManagedObjectModel. In general, the richer the model, the better Core Data is able to support your application.

CD的大部分功能是基于你创建的描述你的应用的实体的图表,它们的属性,和它们之间的关系。CD使用一个被称作 管理对象模型 -一个NSManagedObjecctModel 实例的图表。总的来说,模型越丰富,CD支持你的应用越好。


A managed object model allows Core Data to map from records in a persistent store to managed objects that you use in your application. The model is a collection of entity description objects (instances of NSEntityDescription). An entity description describes an entity (which you can think of as a table in a database) in terms of its name, the name of the class used to represent the entity in your application, and what properties (attributes and relationships) it has.

一个管理对象模型允许CD去去映射(关联)在持久存储中的记录去管理你在应用中使用的对象。这个模型师一个 实体描述 对象 的集合(NSEntityDescription的实例)。一个 实体描述 描述一个实体(你能把它想象成一个在数据库中的表)依据它的名字,这个类的名字用来展示这个实体在你的应用中,和什么属性(属性和关系)它有。


Creating an Entity and Its Properties

创建一个实体和它的属性

When you start a new project in Xcode and open the template selection dialog, select the Use Core Data checkbox. A source file for the Core Data model is created as part of the template. That source file will have the extension .xcdatamodeld. Select that file in the navigator area to display the Core Data model editor.

当你开始一个项目在Xcode 和打开对话选择模版,选择使用CD复选框,一个源文件为CD 模型被创建作为模版的一部分。这个源文件将有.xcdatamodeld后缀。选择那个文件在导航区域去展示CD模型编辑。

To create an entity

1 Click Add Entity.
A new untitled entity will appear in the Entities list in the navigator area. 

点击添加实体

一个新的无标题实体将出席在实体列表中在导航区域。

2 Select the new untitled entity. 

选择新的无标题实体

3 In the Entity pane of the Data Model inspector, enter the name of the entity, and press Return. 

在CD模型查看实体窗格中,输入实体的名字。点击返回。

To create attributes and relationships for the entity

为实体添加属性和关系

1 With the new entity selected, click the plus(+) sign at the bottom of the appropriate section. 

选中一个新实体,点击相关组底部的+号。
A new untitled attribute or relationship (generically referred to as properties) is added under the Attributes or Relationships section of the editor area. 

一个新的无题属性或关系(通常被称为属性)被添加到编辑区域的属性或关系区域。

2 Select the new untitled property.
The property settings are displayed in the Relationships or Attributes pane of the Data Model inspector. 

选择一个新的无题属性。

这个属性设置被展示到数据模型查看器的窗口的关系或属性

3 Give the property a name and press Return.
The attribute or relationship information appears in the editor area. 

Figure 2-2 shows an entity called Employee, with attributes that describe the employee: date of birth, name, and start date.

给属性一个名字点击确定

这个属性或关系信息展示在边境区域。表2-2使用描述雇工的属性:出生日期,名字,和开始日期。展示一个叫雇工的实体


Figure 2-1Employee entity in the Xcode Data Model editor

 

At this point you have created an entity in the model, but you have not created any data. Data is created later, when you launch your application. Inside of your application these entities will be used as the basis for the creation of managed objects (NSManagedObject instances).

到这里你已经在模型中创建一个实体,但是你还有没有创建数据。数据在以后被创建,当你启动你的APP,在你的app中插入的实体将被用来做为基础来创建管理对象(NSManaedObject   instances).


Defining an Entity

Now that you have named your entity, you will define it further in the Entity pane of the Data Model inspector.

现在你已经命名了你的实体,你将在数据模型检查器的实体窗口更进一步的定义它。


Figure 2-2Entity pane in the Data Model inspector

 


Entity Name and Class Name

实体名和类名

Note that the entity name and the class name (a subclass of NSManagedObject) are not the same. The entity structure in the data model does not need to match the class hierarchy. Figure 2-2 shows a class name with the recommended class name pattern of Objective-C, along with an MO suffix. An entity name and a class name are required.

主要实体名和类名(NSManagedObject 的子类)不是相同的。在数据模型中的实体结构不需要去复合类的层级。表2-2展示了一个使用推荐的OC命名模式的类名,有一个MO的后缀。一个实体名字和一个类名是需要的。


Abstract Entities

抽象实体

Specify that an entity is abstract if you will not create any instances of that entity. You typically make an entity abstract if you have a number of entities that all represent specializations of (inherit from) a common entity that should not itself be instantiated. For example, in the Employee entity you could define Person as an abstract entity and specify that only concrete subentities (Employee and Customer) can be instantiated. By marking an entity as abstract in the Entity pane of the Data Model inspector, you are informing Core Data that it will never be instantiated directly.

如果你不创建任何这个实体的实例的时候你可以把这个指定一个实体是抽象。你创建一个抽象实体 如果你有一批的实体都展示特殊化的(继承)一个共同的自己不应该被实例化的实体。比如,在雇员实体应该定义一个人做为一个抽象实体 和 明确只有 具体化的子实体 (雇员和客户) 能被实体化。通过让一个实体作为抽象实体在数据模型查看器的实体窗口中,你通知了DC这个实体用远不会被直接实体化。


Entity Inheritance

Entity inheritance works in a similar way to class inheritance, and is useful for the same reasons. If you have a number of entities that are similar, you can factor the common properties into a superentity, also known as a parent entity. Rather than specifying the same properties in several entities, you can define them in one entity, and the subentities inherit them. For example, you might define a Person entity with attributes firstName and lastName, and subentities Employee and Customer, which inherit those attributes. An example of this layout is shown in Figure 2-3. Display the layout diagram by clicking on the Editor Style toggle switch in the lower right corner.

In many cases, you also implement a custom class to correspond to the entity from which classes representing the subentities also inherit. Rather than implementing business logic common to all the entities several times over, you implement them in one place and they are inherited by the subclasses.

实体继承工作方式和类的继承是相似的,因为相同的原因很有用。如果你有一批的实体是相似的,你能把相同的属性工厂化到一个父实体中,又称作为一个父实体。比起在几个实体中明确相同的属性。你在一个实体中定义它们,和子类实体继承它们。比如你可能使用属性firstName 和lastName定义一个人,和子实体雇员和客户类继承哪些属性,一个这个的布局例子被战胜在表2-3.通过点击在右下角低处的编辑风格切换开关展示布局表。

在很多情况下。你也实例一个自定义类去和实体通信从那个实体展示子实体的类也继承。而不是实现业务逻辑对所有的实体很多次,你实现它们在一个地方和它被之类继承。


NOTE

Be careful with entity inheritance when working with SQLite persistent stores. All entities that inherit from another entity will exist within the same table in SQLite. This factor in the design of the SQLite persistent store can create a performance issue.

使用实体继承要当心在你使用SQLite持久存储的时候。所有从另一个实体继承的实体将存在于SQLite中的相同的表上。这个因素在设计SQLite 持久化存储中能造成性能问题。



Figure 2-3Entity inheritance diagram

 





Defining Attributes and Relationships

定义属性和关系

An entity’s properties are its attributes and relationships, including its fetched properties (if it has any). Among other features, each property has a name and a type. A property name cannot be the same as any no-parameter method name of NSObject or NSManagedObject — for example, you cannot give a property the name “description” (see NSPropertyDescription).

Transient attributes are properties that you define as part of the model, but which are not saved to the persistent store as part of an entity instance’s data. Core Data does track changes you make to transient properties, so they are recorded for undo operations. You use transient properties for a variety of purposes, including keeping calculated values and derived values.


一个实体的属性是它的属性和关系,包括它的获取属性(如果它们任何的)。在其它的特征中,每一个属性有一个名字和一个类型。一个属性名不能是和任何NSObject和NSManagedObject的非参数方法名称相同。比如你不能给一个属性命名为”description”(参见NSPropertyDescritpion).


Transient attributes;

暂时属性是你定义作为模型一部分的属性,它不会被保存到持久存储 作为实体实例数据。CD会追踪你让暂时属性发生的改变,所以它们被记录为撤销操作。你使用暂时属性达到很多的目的,包括保存计算的值和获得值。


NOTE

If you undo a change to a transient property that uses nonmodeled information, Core Data does not invoke your set accessor with the old value — it simply updates the snapshot information.

如果你撤销一个对使用非模型信息的暂时属性的改变,CD不会使用旧值唤醒你的设置附件-它只是简单更新快照信息。


Figure 2-4Attribute pane in the Data Model inspector


Attributes

属性

To define an attribute, select it in the Core Data model editor and specify values in the Attribute pane of the Core Data Model inspector. Core Data natively supports a variety of attribute types, such as string, date, and integer (represented as instances of NSString, NSDate and NSNumber respectively).

You can specify that an attribute is optional — that is, it is not required to have a value. In general, however, you are discouraged from doing so, especially for numeric values. Typically, you can get better results using a mandatory attribute with a default value — defined in the attribute — of 0. The reason for this is that SQL has special comparison behavior for NULL that is unlike Objective-C’s nil. NULL in a database is not the same as 0, and searches for 0 will not match columns with NULL. Moreover, NULL in a database is not equivalent to an empty string or empty data blob.

去定义一个属性,在CD模型编辑器中选择它和在CD模型检查器的属性窗口明确它的值。CD仅仅支持固有的几种属性类型,比如字符串,日期,整数(分别作为NSString,NSDate和NSNumber的实例展示)。你能明确一个属性是可选的-也就是,它不是必须有一个值的。通常来说,最好别这样做,特别是数字类型的值。你能 使用一个强制的有默认值- 定义在属性中的-0 的属性 获得更好的结果。这样做的原因是SQL有特别的针对NULL而不像Objective-C中的nil  的对比行为。NULL在一个数据基库中是和0不同的,搜索0将不会匹配为NULL的列。此外,NULL在一个数据基库和一个空字符串或空数据团是不同的。


Relationships and Fetched Properties

关系和获取属性


To define a relationship, select it in the Core Data model editor, and specify values in the Relationship pane of the Data Model inspector.

去定义一个关系,选择它在CD模型编辑器中,和明确值在数据模型检查器的关系窗口中。


Figure 2-5Relationship in the Data Model inspector

 

Core Data supports to-one and to-many relationships, and fetched properties. Fetched properties represent weak, one-way relationships. In the employees and departments domain, a fetched property of a department might be “recent hires” (employees do not have an inverse to the recent hires relationship).

CD支持到 一个 和 到多个 关系,和读取属性。获取属性展示弱的,单-路关系,在雇员和部门作用范围。一个部分的一个获取属性可能被“暂时雇佣”(雇员没有一个到暂时关系 的反向)。


The Type field defines whether the relationship is a to-one type relationship or a to-many type relationship. Relationships are defined from one direction at a time. To create a many-to-many relationship, you would need to create two to-many relationships and then set them up as inverses of each other.

The Destination field defines what object (or objects) are returned when the relationship is accessed in code. If the relationship is defined as to-one then a single object (or nil if the relationship can be optional) will be returned. If the relationship is defined as to-many, then a set will be returned (or again, nil if the relationship can be optional).

Type 类型区域定义是否 关系是一个 对一类型或是一个对多类型关系。关系在同一时间只能定义一个方向。去创建一个 多对多关系,你将需要创建 两对多 关系然后设置它们彼此之间的可逆。

Destination 区域定义什么对象(或对象们)被返回当关系被访问在代码中。如果关系被定义为 对一 然后一个单独的对象(或nil如果关系是可选的)将被返回。如果关系被定义为 对多,然后一个集合被返回(或再一次,nil 如果关系是可选的)。


The Inverse field defines the other half of a relationship. Because each relationship is defined from one direction, this field joins two relationships together to create a fully bidirectional relationship.

Inverse 区域定义一个关系的另一半。因为每个关系被从一个方向定义,这个区域结合两个关系到一起去创建一个充分的双向关系。

Relationships are described in greater detail in Creating Managed Object Relationships.




















Initializing the Core Data Stack

初始化CD栈

The Core Data stack is a collection of framework objects that are accessed as part of the initialization of Core Data and that mediate between the objects in your application and external data stores. The Core Data stack handles all of the interactions with the external data stores so that your application can focus on its business logic. The stack consists of three primary objects: the managed object context (NSManagedObjectContext), the persistent store coordinator (NSPersistentStoreCoordinator), and the managed object model (NSManagedObjectModel), discussed later in this chapter.

CD栈是一个框架对象的集合 作为CD的实例化的一部分被访问和在你的应用和扩展数据存储之间调解。CD栈处理所有和外部数据存储的交互以至于你能专注在它的业务逻辑上。这个栈持续持有三个重要的对象。管理对象上下文(NSManagedObjectContext),持久存储协调器(NSPersistentStoreCoordinator),和管理对象模型(NSManagedObjectModel),在这个章节的以后讨论。


You initialize the Core Data stack prior to accessing your application data. The initialization of the stack prepares Core Data for data requests and the creation of data. Here’s an example of how to create that Core Data stack.

你初始化CD栈首先去访问你的应用数据。栈的初始化准备CD为数据请求和数据的创建。这里有一个如何去创建CD栈的方法。


Listing 3-1Example Core Data stack creation


OBJECTIVE-C

@interface MyDataController : NSObject

 

@property (strong) NSManagedObjectContext *managedObjectContext;

 

- (void)initializeCoreData;

 

@end

 

@implementation MyDataController

 

- (id)init

{

    self = [super init];

    if (!self) return nil;

 

    [self initializeCoreData];

 

    return self;

}

 

- (void)initializeCoreData

{

    NSURL *modelURL = [[NSBundle mainBundle] URLForResource:@"DataModel" withExtension:@"momd"];

    NSManagedObjectModel *mom = [[NSManagedObjectModel alloc] initWithContentsOfURL:modelURL];

    NSAssert(mom != nil, @"Error initializing Managed Object Model");

 

    NSPersistentStoreCoordinator *psc = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:mom];

    NSManagedObjectContext *moc = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];

    [moc setPersistentStoreCoordinator:psc];


    [self setManagedObjectContext:moc];

es 

    NSFileManager *fileManager = [NSFileManager defaultManager];

    NSURL *documentsURL = [[fileManager URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask] lastObject];

    NSURL *storeURL = [documentsURL URLByAppendingPathComponent:@"DataModel.sqlite"];

 

    dispatch_async(dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(void) {

        NSError *error = nil;

        NSPersistentStoreCoordinator *psc = [[self managedObjectContext] persistentStoreCoordinator];

        NSPersistentStore *store = [psc addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeURL options:options error:&error];

        NSAssert(store != nil, @"Error initializing PSC: %@\n%@", [error localizedDescription], [error userInfo]);

    });

}

SWIFT

import UIKit

import CoreData

class DataController a href="" NSObject /a  {

    var managedObjectContext a href="" NSManagedObjectContext /a 

    init() {

        // This resource is the same name as your xcdatamodeld contained in your project.

        guard let modelURL = NSBundle.mainBundle().URLForResource("DataModel", withExtension:"momd") else {

            fatalError("Error loading model from bundle")

        }

        // The managed object model for the application. It is a fatal error for the application not to be able to find and load its model.

        guard let mom = NSManagedObjectModel(contentsOfURL: modelURL) else {

            fatalError("Error initializing mom from: \(modelURL)")

        }

        let psc = NSPersistentStoreCoordinator(managedObjectModel: mom)

        managedObjectContext = NSManagedObjectContext(concurrencyType: .MainQueueConcurrencyType)

        managedObjectContext.persistentStoreCoordinator = psc

        dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0)) {

            let urls = NSFileManager.defaultManager().URLsForDirectory(.DocumentDirectory, inDomains: .UserDomainMask)

            let docURL = urls[urls.endIndex-1]

            /* The directory the application uses to store the Core Data store file.

            This code uses a file named "DataModel.sqlite" in the application's documents directory.

            */

            let storeURL = docURL.URLByAppendingPathComponent("DataModel.sqlite")

            do {

                try psc.addPersistentStoreWithType(NSSQLiteStoreType, configuration: nil, URL: storeURL, options: nil)

            } catch {

                fatalError("Error migrating store: \(error)")

            }

        }

    }

}






This example creates a controller object that represents the persistence layer of the application. The controller is initialized with a default init call. As part of that init call, the initializeCoreData method is called, and it then proceeds to create the Core Data stack.

这个例子创建一个控制器对象展示了应用的持久层。这个控制器被使用一个默认的init调用创建。作为init调用的一部分,initializeCoreData方法被调用,然后进程去创建了这个CD栈。


NSManagedObjectModel

The NSManagedObjectModel instance describes the data that is going to be accessed by the Core Data stack. During the creation of the Core Data stack, the NSManagedObjectModel (often referred to as the “mom”) is loaded into memory as the first step in the creation of the stack. The example code above resolves an NSURL from the main application bundle using a known filename (in this example DataModel.momd) for the NSManagedObjectModel. Once the NSManagedObjectModel object is initialized, the NSPersistentStoreCoordinator object is constructed.

***NSManagedObjectModel实例描述了将要被CD栈访问的数据。 ***在CD栈的创建期间,这个NSManagedObjectModel(通常叫做“mom”)被加载到内存中作为栈的创建的第一步。上面的示例代码从主应用bundle中使用一个文件名(在这个例子中是DataModel.momd)得到一个NSURL 为 NSManagedObjectModel.一旦这个NSManagedObjectModel对象被初始化,这个NSPersistentStoreCoordinator对象被构造。



NSPersistentStoreCoordinator

The NSPersistentStoreCoordinator sits in the middle of the Core Data stack. The coordinator is responsible for realizing instances of entities that are defined inside of the model. It creates new instances of the entities in the model, and it retrieves existing instances from a persistent store (NSPersistentStore). The persistent store can be on disk or in memory. Depending on the structure of the application, it is possible, although uncommon, to have more than one persistent store being coordinated by the NSPersistentStoreCoordinator.

Whereas the NSManagedObjectModel defines the structure of the data, the NSPersistentStoreCoordinator realizes objects from the data in the persistent store and passes those objects off to the requesting NSManagedObjectContext. The NSPersistentStoreCoordinator also verifies that the data is in a consistent state that matches the definitions in the NSManagedObjectModel.

这个NSPersistentStoreCoordinator在CD栈的中间。这个协调器负责在模型中的实体的实例化。它创建在模型中实体的实例,和它从持久存储(NSPersistentStore)中获取存在的实例。这个持久存储能在磁盘上或在内存中。取决于应用的结构,有多个持久存储同时被NSPersistentStoreCoordinator协调是可能的,经过不常见。

***鉴于NSManagedObjectModel定义了数据的结构***,

***NSPersistentStoreCoordinator 从在持久存储数据中实例对象和传输哪些对象到请求 NSManagedObjectContext***.这个NSPersistentStoreCoordinator 也验证在持久状态的匹配在NSManagedObjectModel定义的数据。


NSManagedObjectContext

The managed object context (NSManagedObjectContext) is the object that your application will be interacting with the most, and therefore it is the one that is exposed to the rest of your application. Think of the managed object context as an intelligent scratch pad. When you fetch objects from a persistent store, you bring temporary copies onto the scratch pad where they form an object graph (or a collection of object graphs). You can then modify those objects however you like. Unless you actually save those changes, however, the persistent store remains unaltered.

这个管理对象上写文(NSManagedObjectContext)是你的应用将要交互最多的对象,因此它是暴露在你应用程序剩余部分。把管理对象上下文是一个有智慧的便签本。***当你从一个持久存储获取对象时,你吧对象图表(或对象图表的集合)暂时拷贝到便签本上。你可以随意的改变哪些对象。除非你真正的保存哪些改变,否则持久存储保持不变***。

All managed objects must be registered with a managed object context. You use the context to add objects to the object graph and remove objects from the object graph. The context tracks the changes you make, both to individual objects’ attributes and to the relationships between objects. By tracking changes, the context is able to provide undo and redo support for you. It also ensures that if you change relationships between objects, the integrity of the object graph is maintained.

所有管理对象必须使用一个管理对象上下文注册。你使用上下文去添加对象到对象图表和从对象图表中移除对象。这个上下文追逐你做的改变,不管上单个 对象的属性和对象之间的关系。通过追踪改变,上下文能够提供给你重复和撤销的操作。它也确保如果你改变对象之间的关系,对象图表的完整形被保持。




If you choose to save the changes you have made, the context ensures that your objects are in a valid state. If they are, the changes are written to the persistent store (or stores), new records are added for objects you created, and records are removed for objects you deleted.

如果你选择去保存你做的改变,上写文确保你的对象在一个可用的状态。如果它们在,这个改变被写入到持久存储(或存储)中,新的记录被添加到你创建的对象,你删除对象的记录被删除。

Without Core Data, you have to write methods to support archiving and unarchiving of data, to keep track of model objects, and to interact with an undo manager to support undo. In the Core Data framework, most of this functionality is provided for you automatically, primarily through the managed object context.

没有CD,你不得不去写方法去支持数据的归档接档,去保持模型对象的追踪,和去和一个撤销管理去支持撤销。在CD框架中,很多的这个功能为你自动的提供,通过管理对象上下文。





 




























Creating and Saving Managed Objects


Once you have defined your managed object model and initialized the Core Data stack within your application, you are ready to start creating objects for data storage.

一旦你已经定义了管理对象模型和初始化CD栈在你的应用中,你已经准备开始为数据存储创建对象。


Creating Managed Objects

创建管理对象


An NSManagedObject instance implements the basic behavior required of a Core Data model object. The NSManagedObject instance requires two elements: an entity description (an NSEntityDescription instance) and a managed object context (an NSManagedObjectContext instance). The entity description includes the name of the entity that the object represents and its attributes and relationships. The managed object context represents a scratch pad where you create the managed objects. The context tracks changes to and relationships between objects.


***一个NSManageObject实例实现一个CD模型对象的获取基本行为***。

NSManagedObject实例需要两个元素:

1.一个实体描述(一个NSEntityDescription实例)

2.一个管理对象上下文(一个NSManagedObjectContext 实例)。

这个实体描述包含展示对象的名字关系的实体的名字。

这个管理对象上下文展示一个你能创建管理对象的便签本。这个上下文追逐在对象之间的改变。


As shown in this example, the NSEntityDescription class has a class method that accepts a string for the name of the entity and a reference to the NSManagedObjectContext that the NSManagedObject instance will be associated with. The example defines the returning object as AAAEmployeeMO object.

像这个例子中显示的一样,这个NSEntityDescription 类有一个类方法它接收一个字符串作为实体的名字和引用到NSManagedObject 实例将协助的NSManagedObjectContext。这个例子定义了返回对象作为一个AAAEmplayeeMo对象。

OBJECTIVE-C

AAAEmployeeMO employee = [NSEntityDescription insertNewObjectForEntityForName:@"Employee" inManagedObjectContext:[self managedObjectContext];

SWIFT

let employee = NSEntityDescription.insertNewObjectForEntityForName("Employee", inManagedObjectContext: self.managedObjectContext) as a href="" AAAEmployeeMO /a 



Creating NSManagedObject Subclasses

By default, Core Data will return NSManagedObject instances to your application. However, it is useful to define subclasses of NSManagedObject for each of the entities in your model. Speciflcally, when you create subclasses of NSManagedObject, you can define the properties that the entity can use for code completion and you can add convenience methods to those subclasses.

To create a subclass of NSManagedObject, in Xcode’s Core Data model editor, select the entity, and in the Entity pane of the Data Model inspector, enter the name in the Class field. Then create the subclass (AAAEmployeeMO) in Xcode.

默认情况。CD将返回NSManagedObject对象到你的应用。然而,定义NSManagedObject的之类为每一个在你的模型中的实体很有用。特别是,当你创建NSManagedObject的子类,你能定义实体能用代码完成属性和你能添加便利方法到它的子类。去创建NSManagedObject的子类,在Xcode的CD模型编辑器,选择实体,和在数据模型查看器的实体窗口,输入名字在 Class 区域。然后创建子类在Xcode中。

OBJECTIVE-C

#import <CoreData/CoreData.h>

 

@interface AAAEmployeeMO : NSManagedObject

 

@property (nonatomic, strong) NSString *name;

 

@end

 

@implementation AAAEmployeeMO

 

@dynamic name;

 

@end

SWIFT

import UIKit

import CoreData

import Foundation

 

class AAAEmployeeMO a href="" NSManagedObject /a  {

   

    @NSManaged var name a href="" String /a ?

   

}

The @dynamic tag informs the compiler that the variable will be resolved at runtime.

***这个@dynaic标签通知编译器这个变量将被在运行时解析***。

Once the subclass has been defined in your data model and added to your project, you can reference it directly in your application and improve the readability of your application code.

一旦子类已经在你的数据模型中被定义和添加到你的项目中。你能在你的项目中直接引用它和提高你应用代码的可读性。


Saving NSManagedObject Instances

保持NSManagedObject 实例

The creation of NSManagedObject instances does not guarantee their persistence. Once you create an NSManagedObject instance in your managed object context, you must explicitly save that context to persist those changes to your persistent store.

NSManagedObject实例的创建不保证它的持久化一旦你创建一个NSManagedObject实例在你的管理对象上写文中,你必须明确保持那个上下去持久化哪些改变到你的持久存储中。


OBJECTIVE-C

NSError *error = nil;

if ([[self managedObjectContext] save:&error] == NO) {

    NSAssert(NO, @"Error saving context: %@\n%@", [error localizedDescription], [error userInfo]);

}

SWIFT

do {

    try managedObjectContext.save()

} catch {

    fatalError("Failure to save context: \(error)")

}




The call to save on the NSManagedObjectContext accepts a reference to an NSError variable and always returns either a success or a fail. If the save fails, it is important to display the error condition so that it can be corrected. The display of that error condition can be as simple as outputting the error to the console or as complicated as offering the error message to the user. If the save method returns a success, then the error variable does not need to be consulted.

去保存到NSManagedObjectContext的方法接受一个到一个NSError变量的引用和总是返回一个错误和失败。如果保存失败,去展示错误情况来使错误被改正是重要的。错误的情况展示像输出错误到控制台一样简单或像提供错误信息给用户一样复杂。如果保存方法返回成功,然后错误变量不需要被查看。







Creating and Modifying Custom Managed Objects


As discussed previously, managed objects are instances of the NSManagedObject class, or of a subclass of NSManagedObject, that represent instances of an entity. NSManagedObject is a generic class that implements all the basic behavior required of a managed object. You can create custom subclasses of NSManagedObject, although this is often not required. If you do not need any custom logic for a given entity, you do not need to create a custom class for that entity. You implement a custom class to, for example, provide custom accessor or validation methods, use nonstandard attributes, specify dependent keys, calculate derived values, and implement any other custom logic.

正如上面讨论的,管理对象是展示一个实体的例子 ,是NSManagedObject类的实例,或 NSManagedObject 子类的。NSManagedObject是一个普通实现一个管理对象的所有基本需要行为的类。你能创建自定义的NSManagedObject的子类,尽管这个通常不需要。如果你不需要任何自定义逻辑为一个给定实体,你不需要为那个实体创建一个自定义的类。你实现一个自定义类去,比如提供自定义的访问或验证方法,使用非标准的属性,明确独立keys,计算衍生值,和实现你任何其它自定逻辑。


Creating Custom Managed Object Subclasses

自定义管理对象子类


In an Objective-C managed object subclass, you can declare the properties for modeled attributes in the interface file, but you don’t declare instance variables:

在一个Objective-C管理对象子类中,你能声明为在文件接口中模型化属性声明属性,但是你不声明实例变量:

@interface MyManagedObject : NSManagedObject

 

@property (nonatomic, strong) NSString *title;

@property (nonatomic, strong) NSDate *date;

 

@end

Notice that the properties are declared as nonatomic and strong. For performance reasons, Core Data typically does not copy object values, even if the value class adopts the NSCopying protocol.

注意属性被声明为nonatomic 和strong.因为性能原因,CD不会拷贝对象值,就算值类采用了NSCopying 协议。


In the Objective-C implementation file, you specify the properties as dynamic:

在Objective-C实现文件中,你明确属性为dynamic(动态的)。

@implementation MyManagedObject

@dynamic title;

@dynamic date;

@end

In Swift, you declare the properties using the @NSManaged keyword:

class MyManagedObject a href="" NSManagedObject /a  {

    @NSManaged var title a href="" String /a ?

    @NSManaged var date a href="" NSDate /a ?

}


Core Data dynamically generates efficient public and primitive get and set attribute accessor methods and relationship accessor methods for properties that are defined in the entity of a managed object’s corresponding managed object model. Therefore, you typically don’t need to write custom accessor methods for modeled properties.

CD动态生成高效公共的和原始获取和设置属性访问方法和关系访问方法 为 定义在一个管理对象的相关的管理对象模型 实体中的属性。***因此你不需要为模型化的属性写自定义的访问方法。





Guidelines for Overriding Methods

NSManagedObject itself customizes many features of NSObject so that managed objects can be properly integrated into the Core Data infrastructure. Core Data relies on NSManagedObject’s implementation of the following methods, which you should therefore not override:

NSManagedObject它自己自定义很多NSObject的特征所以管理对象能被合适的整合到CD基础设置中。***CD依赖NSManagedObject如下方法的实现,这些方法是不允许被重写的***。


primitiveValueForKey: 

setPrimitiveValue:forKey: 

isEqual: 

hash 

superclass 

class 

self 

zone 

isProxy 

isKindOfClass: 

isMemberOfClass: 

conformsToProtocol: 

respondsToSelector: 

managedObjectContext 

entity 

objectID 

isInserted 

isUpdated 

isDeleted 

isFault 

You are discouraged from overriding initWithEntity:insertIntoManagedObjectContext: and description. If description fires a fault during a debugging operation, the results may be unpredictable. You should typically not override the key-value coding methods such as valueForKey: and setValue:forKeyPath:.

In addition, before overriding awakeFromInsert, awakeFromFetch, and validation methods such as validateForUpdate:, invoke their superclass implementation. Be careful when overriding accessor methods because you could negatively impact performance.

不能从写initWithEntity:insertIntoManagedObjectContext:和description.如果description产生一个错误在调试操作中,结果是不可预知的。尤其不能重写key-value编码方法,比如valueForKey:和 setValue:forKeyPath:

另外在重写awakeFromInsert,awakeFromFetch之前,和验证方法比如validateForUpdate:唤醒它们的父类实现。当重些访问方法时要小心因为你肯那个负面影响执行。





Defining Properties and Data Storage

定义属性和数据存储

In some respects, a managed object acts like a dictionary—it is a generic container object that efficiently provides storage for the properties defined by its associated NSEntityDescription object. NSManagedObject supports a range of common types for attribute values, including string, date, and number (see NSAttributeDescription for full details). Therefore, you typically do not need to define instance variables in subclasses. However, if you need to implement nonstandard attributes or preserve timezones, you may need to do so. In addition, there are some performance considerations that can be mitigated in a subclass if you use large binary data objects—see Binary Large Data Objects (BLOBs).

在一些方面,一个关系对象表现的像一个字典-它是一个通用容器对象 高效提供存储为被相关NSEntityDescription对象定义的属性。NSManagedObject 支持一系列的通用类型 为属性值,包括字符串,日期,和数字(参见NSAttributeDescription得到所有细节)。因此,你不需要去定义示例变量在子类中。然而如果你需要去实现非标准属性或保存时区,你可能需要这样做。另外,有一些执行考虑 如果你使用大的二进制数据对象 就会减轻执行压力。


Using Nonstandard Attributes

使用非标准属性

By default, NSManagedObject stores its properties as objects in an internal structure, and in general Core Data is more efficient working with storage under its own control than with using custom instance variables.

Sometimes you need to use types that are not supported directly, such as colors and C structures. For example, in a graphics application you might want to define a Rectangle entity that has attributes color and bounds, which are instances of NSColor and an NSRect structures respectively. This situation requires you to create a subclass of NSManagedObject.

通过默认,NSManagedObject 存储它的属性作为在内在结构中的对象,通常来说CDCD使用在它自己控制的存储会更高效相比于使用自定义的实力变量。有时你需要去使用不是直接支持的类型,比如颜色或C  结构体。比如在一个图表应用中你可能想去定义一个有属性为颜色和边界的矩形实体,它们各自是NSColor的实例 和一个NSRect结构体。这个情况要求i去创建一个NSManagedObject子类。


Dates, Times, and Preserving Timezones

日期,时间,和保存时区

NSManagedObject represents date attributes with NSDate objects, and stores times internally as an NSTimeInterval value which is based on GMT. Time zones are not explicitly stored — indeed you should always represent a Core Data date attribute in GMT, so that searches are normalized in the database. If you need to preserve the time zone information, you need to store a time zone attribute in your model, which may require you to create a subclass of NSManagedObject.

NSManagedObject展示使用一个NSDate对象日期属性,内部存储时间作为一个基于GMT的NSTimeInterval值。时区不会被精确存储-确实你应该展示一个CD时间属性在GMT,使搜索在数据中被规范化。如果你需要去保存时区信息,你需要去存储时区属性在你的模型,这可能需要去创建NSManagedObject的子类。


Customizing Initialization and Deallocation

自定义初始化和释放


Core Data controls the life cycle of managed objects. With faulting and undo, you cannot make the same assumptions about the life cycle of a managed object that you do with a standard Objective-C object — managed objects can be instantiated, destroyed, and resurrected by the framework as it requires.

CD控制管理对象的生命周期。有故障和撤销,你不能设置相同的关于你在标准Objective-C对象管理对象生命周期假设-管理对象能被创建,销毁,和通过框架复苏 如果它需要。





When a managed object is created, it is initialized with the default values given for its entity in the managed object model. In many cases the default values set in the model are sufficient. Sometimes, however, you may wish to perform additional initialization—perhaps using dynamic values (such as the current date and time) that cannot be represented in the model.

当一个管理对象被创建,它被创建 使用默认值 给它的在管理对象模型实体。在很多场合默认值设置在模型中是充足的。有时,然后你可能希望去执行额外的初始化-可能使用动态值(比如当前日期和时间)不能够被在模型中体现。


In a typical Objective-C class, you usually override the designated initializer (often the init method). In a subclass of NSManagedObject, there are three different ways you can customize initialization — by overriding initWithEntity:insertIntoManagedObjectContext:, awakeFromInsert, or awakeFromFetch. Do not override init. You are discouraged from overriding initWithEntity:insertIntoManagedObjectContext:, because state changes made in this method may not be properly integrated with undo and redo. The two other methods, awakeFromInsert and awakeFromFetch, allow you to differentiate between two different situations:

在一个典型Objective-C类,你通常重写定义的指定的初始化的(通常是init方法)。在一个NSManagedObject的子类,有三个你能自定义初始化的的不同方法。通过重写initWithEntity:insertIntoManagedObjectContext:,awakeFromInsert,或awakeFromFetch.不要从写init.不鼓励重写initWithEntity:insertIntoManagedObjectContext:,因为状态改变在这个方法可能不被适当的使用撤销和重做整合。其它两个方法,awakeFromInsert和awakeFromFetch,允许你在不同情况下区分:


awakeFromInsert is invoked only once in the lifetime of an object—when it is first created. 

awakeFromInsert被唤醒仅一次一个对象在生命周期中-当它第一次被创建。


awakeFromInsert is invoked immediately after you invoke initWithEntity:insertIntoManagedObjectContext: or insertNewObjectForEntityForName:inManagedObjectContext:. You can use awakeFromInsert to initialize special default property values, such as the creation date of an object, as illustrated in the following example. 

awakeFromInsert被立刻唤醒在你唤醒initWithEntity:insertIntoManagedObjectContext:或insertNewObjectForEntityForName:inManagedObjectContext:.你能使用awakeFromInsert去初始化特殊模型属性值,比如一个对象的日期创建,在以后的例子中说明。





OBJECTIVE-C

- (void)awakeFromInsert

{

    [super awakeFromInsert];

    [self setCreationDate:[NSDate date]];

}


SWIFT

override func awakeFromInsert() {

    super.awakeFromInsert()

    creationDate = NSDate()

}



awakeFromFetch is invoked when an object is reinitialized from a persistent store (during a fetch). 

awakeFromFetch 被唤醒当对象被再次初始化从一个持久存储中(在获取期间)。
You can override
awakeFromFetch to, for example, establish transient values and other caches. Change processing is explicitly disabled in awakeFromFetch so that you can conveniently use public set accessor methods without dirtying the object or its context. This disabling of change processing does mean, however, that you should not manipulate relationships, as changes will not be properly propagated to the destination object or objects. Instead of overriding awakeFromFetch, you can override awakeFromInsert or employ any of the run loop-related methods such as performSelector:withObject:afterDelay:

你能重写awakeFromFetch,比如,建立瞬时值和其它高速缓存。改变处理是明确禁用的 在awakeFromFetch 所以你能方便使用公共设置方法而不用污染对象或它的上下文。这个改变线程的不可用不意味着,然而。你不应该操作关系,因为改变将被适当的传播到目的对象。而不是重写awakeFromFetch,你能重写awakeFromInsert或使用任何与运行循环相关的方法比如performSelector:withObject:afterDelay:




Avoid overriding dealloc to clear transient properties and other variables. Instead, override didTurnIntoFault. didTurnIntoFault is invoked automatically by Core Data when an object is turned into a fault and immediately prior to actual deallocation. You might turn a managed object into a fault specifically to reduce memory overhead (see Reducing Memory Overhead), so it is important to ensure that you properly perform cleanup operations in didTurnIntoFault.

避免重写dealloc去清除暂时属性和其它变量。

***而是重写didTurnIntoFault.didTurnIntoFault 被CD自动唤醒当对象被转入到错误和立即在实际中释放。你可能转化一个管理对象到一个错误 特别是去减少内存过多(参见Reducing Memory Overhead),所以去保证你属性执行清除操作在didTurnIntoFault是重要的***。









Fetching Objects

获取对象

Now that data is stored in the Core Data persistent store, you will use an NSFetchRequest to access that existing data. The fetching of objects from Core Data is one of the most powerful features of this framework.

现在数据被存储在CD持久存储中,你将使用一个NSFetchRequest去访问存在的数据。从CD获取对象是这个框架中最牛逼的特征。


Fetching NSManagedObject Instances

获取MO实例

In this example you start by constructing an NSFetchRequest that describes the data you want returned. This example does not add any requirements to that data other than the type of entity being returned. You then call executeFetchRequest:error: on the NSManagedObjectContext and pass in the request along with a pointer to an error.

在这个例子中你通过构造一个描述你想返回的数据的NSFetchRequest开始。这个例子没有添加任何请求到数据除了被返回实体的类型。你然后调用executeFetchRequest:error:在NSManagedObjectContext和传入请求和一个错误指针。


OBJECTIVE-C

NSManagedObjectContext *moc = …;

NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:@"Employee"];

 

NSError *error = nil;

NSArray *results = [moc executeFetchRequest:request error:&error];

if (!results) {

    DLog(@"Error fetching Employee objects: %@\n%@", [error localizedDescription], [error userInfo]);

    abort();

}

SWIFT

let moc = managedObjectContext

let employeesFetch = NSFetchRequest(entityName: "Employee")

 

do {

    let fetchedEmployees = try moc.executeFetchRequest(employeesFetch) as! [ a href="" AAAEmployeeMO /a ]

} catch {

    fatalError("Failed to fetch employees: \(error)")

}

The executeFetchRequest:error: method has two possible results. It will either return an NSArray object with zero or more objects, or it will return nil. If nil is returned, you have received an error from Core Data and need to respond to it. If the array exists, you receive possible results for the request even though the NSArray may be empty. An empty NSArray indicates that there were no records found.

这个executeFetchRequest:error:方法有两个可能结果。它将返回一个有零个或多个对象的NSArray对象,或它将返回nil.如果nil被返回,你已经从CD收到一个错误和需要去响应它,如果数组存在,你接受可能的请求的结果尽管NSArray可能为空。一个空数组意味着没有记录被查找到。









Filtering Results

过滤结果

The real flexibility in fetching objects comes in the complexity of the fetch request. To begin with, you can add an NSPredicate object to the fetch request to narrow the number of objects being returned. For example, if you only want Employee objects that have a firstName of Trevor, you add the predicate directly to NSFetchRequest:

在获取对象的真正的灵活性来自愈获取请求的复杂性。开始使用你能添加NSPredicate对象到获取请求去减少被返回对象的数量。比如你想要的雇员对象有Trevor作为firstName,直接添加谓词到NSFetchRequest:


OBJECTIVE-C

NSString *firstName = @"Trevor";

[fetchRequest setPredicate:[NSPredicate predicateWithFormat:@"firstName == %@", firstName]];

SWIFT

let firstName = "Trevor"

fetchRequest.predicate = NSPredicate(format: "firstName == %@", firstName)


In addition to narrowing the objects being returned, you can configure how those objects are returned. For example, you can instruct Core Data to return NSDictionary instances instead of fully formed NSManagedObject instances. Further, you can configure the NSFetchRequest so that those NSDictionary instances only contain a subset of the properties available on the Employee entity.

除了去限制被返回的对象,你能配置哪些对象被如何返回。比如你能知道CD去返回NSDictionary 实例而不是完全形成NSManagedObject实例。

***更深,你能配置NSFetchRequest 所以哪些NSDictionary实例仅仅包含在雇员实体中一个属性变量的子集合***。

For more information about NSFetchRequest, see the class documentation.

获得更多关于NSFetchRequest信息,参见类文档。










Connecting the Model to Views

链接模型到Views

In OS X, Core Data is designed to work with the user interface through Cocoa bindings. However, Cocoa bindings are not a part of the user interface in iOS. In iOS, you use NSFetchedResultsController to connect the model (Core Data) to the views (storyboards).

在OS X,CD被设计通过Cocoa连接使用用户界面去工作。然而,Cocoa连接在iOS中不是用户界面的一部分。在iOS中你使用NSFetchedResultsController去连接模型(Core Data)到Views(故事板上)。


NSFetchedResultsController provides the interface between Core Data and UITableView objects. Because table views are the most common way to display data in iOS, UITableView handles nearly all high-volume data displays. 

NSFetchedResultsController 提供在CD和UITableView对象之间的界面。因为table view是最常见的在iOS中展示数据的方式,UITableView处理几乎所有高-容量数据展示。


Creating a Fetched Results Controller

创建一个获取结果控制器


Typically, an NSFetchedResultsController instance will be initialized by an UITableViewController instance that is going to utilize it. This initialization can take place in the  viewDidLoad  or  viewWillAppear  methods, or at another logical point in the life cycle of the view controller. This example shows the initialization of the NSFetchedResultsController.

通常,一个NSFetchedResultsController实例将被一个将要使用它UITableViewController实例初始化。这个初始化能发生在ViewDidLoad或viewWillAppear 方法中,或在VC生命循环的 另外逻辑点。这个例子展示NSFetchedResultsController的初始化。


OBJECTIVE-C

@property (nonatomic, strong) NSFetchedResultsController *fetchedResultsController;

 

- (void)initializeFetchedResultsController

{

    NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:@"Person"];

 

    NSSortDescriptor *lastNameSort = [NSSortDescriptor sortDescriptorWithKey:@"lastName" ascending:YES];

 

    [request setSortDescriptors:@[lastNameSort]];

 

    NSManagedObjectContext *moc = …; //Retrieve the main queue NSManagedObjectContext

 

    [self setFetchedResultsController:[[NSFetchedResultsController alloc] initWithFetchRequest:request managedObjectContext:moc sectionNameKeyPath:nil cacheName:nil]];

    [[self fetchedResultsController] setDelegate:self];

 

    NSError *error = nil;

    if (![[self fetchedResultsController] performFetch:&error]) {

        NSLog(@"Failed to initialize FetchedResultsController: %@\n%@", [error localizedDescription], [error userInfo]);

        abort();

    }

}

SWIFT

var fetchedResultsController a href="" NSFetchedResultsController /a !

 

func initializeFetchedResultsController() {

    let request = NSFetchRequest(entityName: "Person")

    let departmentSort = NSSortDescriptor(key: "department.name", ascending: true)

    let lastNameSort = NSSortDescriptor(key: "lastName", ascending: true)

    request.sortDescriptors = [departmentSort, lastNameSort]

   

    let moc = self.dataController.managedObjectContext

    fetchedResultsController = NSFetchedResultsController(fetchRequest: request, managedObjectContext: moc, sectionNameKeyPath: "department.name", cacheName: "rootCache")

    fetchedResultsController.delegate = self

   

    do {

        try fetchedResultsController.performFetch()

    } catch {

        fatalError("Failed to initialize FetchedResultsController: \(error)")

    }

}

In the initializeFetchedResultsController method shown above that will live within the controlling UITableViewController instance, you first construct a fetch request (NSFetchRequest), which is at the heart of the NSFetchedResultsController. Note that the fetch request contains a sort descriptor (NSSortDescriptor). NSFetchedResultsController requires at least one sort descriptor to control the order of the data that is being presented.

在initializeFetchedResultsController方法显示它生存在UITableViewController实例控制中,你***首先构造一个获取请求,它是NSFetchedResultsController的核心。注意那个获取请求包含一个集合描述(NSSortDescriptor).NSFetchedResultsController需要最少一个集合描述去控制被展示数据的顺序。



Once the fetch request is initialized, you can initialize the NSFetchedResultsController instance. The fetched results controller requires you to pass it an NSFetchRequest instance and a reference to the managed object context (NSManagedObjectContext) that the fetch is to be run against. The sectionNameKeyPath and the cacheName properties are both optional.

一旦获取请求被初始化,你能初始化NSFetchedResultsController 实例。这个获取请求控制器需要你去传给它一个NSFetchRequest实例和相关的获取被运行的管理对象上下文(NSManagedObjectContext)。这个sectionNameKeyPath 和cacheName属性都是可选的。


Once the fetched results controller is initialized, you assign it a delegate. The delegate will notify the table view controller when any changes have occurred to the underlying data structure. Typically, the table view controller will also be the delegate to the fetched results controller so that it can receive callbacks whenever there are changes to the underlying data.

一旦获取结果控制器被初始化,你指定它一个代理。这个代理将通知table view 控制器 当任何改变已经发生在底层的数据结构中。通常,table view 控制器将成为获取结果控制器的代理所以它能接受任何时候底层数据发生改变的回调


Next, you start the NSFetchedResultsController by a call to performFetch:. This call retrieves the initial data to be displayed and causes the NSFetchedResultsController instance to start monitoring the managed object context for changes.

下一步,你通过调用performFetch:开启NSFetchedResultsController.这个调用获取初始化的被展示的数据和造成NSFetchedResultsController实例去开始监视管理对象上下文的改变。



Integrating the Fetched Results Controller with the Table View Data Source

整合获取结果控制器和Table View 数据源


After you integrate the initialized fetched results controller and have data ready to be displayed in the table view, you need to integrate the fetched results controller with the table view data source (UITableViewDataSource).

在你整合这个被初始化的获取结果控制器和有了准备被展示在table view 中的数据以后,你需要去整合获取结果控制器和table view 数据源(UITableViewDataSource).



OBJECTIVE-C

#pragma mark - UITableViewDataSource

 

- (void)configureCell:(id)cell atIndexPath:(NSIndexPath*)indexPath

{

    id object = [[self fetchedResultsController] objectAtIndexPath:indexPath];

 

    // Populate cell from the NSManagedObject instance

从管理对象的实例填充cell

}

 

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath

{

    id cell = [tableView dequeueReusableCellWithIdentifier:CellReuseIdentifier];

    // Set up the cell

    [self configureCell:cell atIndexPath:indexPath];

    return cell;

}

 

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView

{

    return [[[self fetchedResultsController] sections] count];

}

 

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section

{

    id< NSFetchedResultsSectionInfo> sectionInfo = [[self fetchedResultsController] sections][section];

    return [sectionInfo numberOfObjects];

}

SWIFT

func configureCell(cell a href="" UITableViewCell /a , indexPath a href="" NSIndexPath /a ) {

    let employee = fetchedResultsController.objectAtIndexPath(indexPath) as a href="" AAAEmployeeMO /a 

    // Populate cell from the NSManagedObject instance

    print("Object for configuration: \(object)")

}

 

override func tableView(tableView a href="" UITableView /a , cellForRowAtIndexPath indexPath a href="" NSIndexPath /a ) ->  a href="" UITableViewCell /a  {

    let cell = tableView.dequeueReusableCellWithIdentifier("cellIdentifier", forIndexPath: indexPath) as a href="" UITableViewCell /a 

    // Set up the cell

    configureCell(cell, indexPath: indexPath)

    return cell

}

 

override func numberOfSectionsInTableView(tableView a href="" UITableView /a ) ->  a href="" Int /a  {

    return fetchedResultsController.sections!.count

}

 

override func tableView(tableView a href="" UITableView /a , numberOfRowsInSection section a href="" Int /a ) ->  a href="" Int /a  {

    let sections = fetchedResultsController.sections as! [ a href="" NSFetchedResultsSectionInfo /a ]

    let sectionInfo = sections[section]

    return sectionInfo.numberOfObjects

}

As shown in each UITableViewDataSource method above, the integration with the fetched results controller is reduced to a single method call that is specifically designed to integrate with the table view data source.


正如上面在每个UITableViewDataSource方法,使用获取结果控制器整合被减少到一个单独的被名曲设计去使用table view 数据源的 整合方法调用


Communicating Data Changes to the Table View

连接数据改变到TableView

In addition to making it significantly easier to integrate Core Data with the table view data source, NSFetchedResultsController handles the communication with the UITableViewController instance when data changes. To enable this, implement the NSFetchedResultsControllerDelegate protocol:

除了让使用table view 数据源整合CD明显简单,NSFetchedResultsController处理当数据改变时和UITableViewController 交流,让这个可用,实现NSFetchedResultsControllerDelegate 协议:

OBJECTIVE-C

#pragma mark - NSFetchedResultsControllerDelegate

- (void)controllerWillChangeContent:(NSFetchedResultsController *)controller

{

    [[self tableView] beginUpdates];

}

- (void)controller:(NSFetchedResultsController *)controller didChangeSection:(id <NSFetchedResultsSectionInfo>)sectionInfo atIndex:(NSUInteger)sectionIndex forChangeType:(NSFetchedResultsChangeType)type

{

    switch(type) {

        case NSFetchedResultsChangeInsert:

            [[self tableView] insertSections:[NSIndexSet indexSetWithIndex:sectionIndex] withRowAnimation:UITableViewRowAnimationFade];

            break;

        case NSFetchedResultsChangeDelete:

            [[self tableView] deleteSections:[NSIndexSet indexSetWithIndex:sectionIndex] withRowAnimation:UITableViewRowAnimationFade];

            break;

        case NSFetchedResultsChangeMove:

        case NSFetchedResultsChangeUpdate:

            break;

    }

}

- (void)controller:(NSFetchedResultsController *)controller didChangeObject:(id)anObject atIndexPath:(NSIndexPath *)indexPath forChangeType:(NSFetchedResultsChangeType)type newIndexPath:(NSIndexPath *)newIndexPath

{

    switch(type) {

        case NSFetchedResultsChangeInsert:

            [[self tableView] insertRowsAtIndexPaths:@[newIndexPath] withRowAnimation:UITableViewRowAnimationFade];

            break;

        case NSFetchedResultsChangeDelete:

            [[self tableView] deleteRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationFade];

            break;

        case NSFetchedResultsChangeUpdate:

            [self configureCell:[[self tableView] cellForRowAtIndexPath:indexPath] atIndexPath:indexPath];

            break;

        case NSFetchedResultsChangeMove:

            [[self tableView] deleteRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationFade];

            [[self tableView] insertRowsAtIndexPaths:@[newIndexPath] withRowAnimation:UITableViewRowAnimationFade];

            break;

    }

}

- (void)controllerDidChangeContent:(NSFetchedResultsController *)controller

{

    [[self tableView] endUpdates];

}

SWIFT

func controllerWillChangeContent(controller a href="" NSFetchedResultsController /a ) {

    tableView.beginUpdates()

}

 

func controller(controller a href="" NSFetchedResultsController /a , didChangeSection sectionInfo a href="" NSFetchedResultsSectionInfo /a , atIndex sectionIndex a href="" Int /a , forChangeType type a href="" NSFetchedResultsChangeType /a ) {

    switch type {

    case .Insert:

        tableView.insertSections(NSIndexSet(index: sectionIndex), withRowAnimation: .Fade)

    case .Delete:

        tableView.deleteSections(NSIndexSet(index: sectionIndex), withRowAnimation: .Fade)

    case .Move:

        break

    case .Update:

        break

    }

}

 

func controller(controller a href="" NSFetchedResultsController /a , didChangeObject anObject a href="" NSManagedObject /a , atIndexPath indexPath a href="" NSIndexPath /a ?, forChangeType type a href="" NSFetchedResultsChangeType /a , newIndexPath a href="" NSIndexPath /a ?) {

    switch type {

    case .Insert:

        tableView.insertRowsAtIndexPaths([newIndexPath!], withRowAnimation: .Fade)

    case .Delete:

        tableView.deleteRowsAtIndexPaths([indexPath!], withRowAnimation: .Fade)

    case .Update:

        configureCell(self.tableView.cellForRowAtIndexPath(indexPath!)!, indexPath: indexPath!)

    case .Move:

        tableView.deleteRowsAtIndexPaths([indexPath!], withRowAnimation: .Fade)

        tableView.insertRowsAtIndexPaths([indexPath!], withRowAnimation: .Fade)

    }

}

 

func controllerDidChangeContent(controller a href="" NSFetchedResultsController /a ) {

    tableView.endUpdates()

}

Implementing the four protocol methods shown above provides automatic updates to the associated UITableView whenever the underlying data changes.

实现上方展示的四个协议方法自动更新与它相关的UITableView 无论何时底层数据改变。









Adding Sections

 添加组

So far you have been working with a table view that has only one section, which represents all of the data that needs to be displayed in the table view. If you are working with a large number of Employee objects, it can be advantageous to divide the table view into multiple sections. Grouping the employees by department makes the list of employees more manageable. Without Core Data, a table view with multiple sections would involve an array of arrays, or perhaps an even more complicated data structure. With Core Data, you make a simple change to the construction of the fetched results controller.

现在你已经使用只有 一个组的 table view和table view 组合在一起工作,这组展示所有的需要被展示到table view上的数据。如果你使用很多的雇员对象工作。把table view 分到很多组会更加的有利。通过部门把雇员分组使雇员清单更好管理。 没有CD,一个使用很多组的table view  将包含数组的数组,又或者一个更加复杂的数据结构。使用CD,你做一个简单的改变到获取结果控制器的结构。

OBJECTIVE-C

- (void)initializeFetchedResultsController

{

    NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:@"Person"];

    NSSortDescriptor *departmentSort = [NSSortDescriptor sortDescriptorWithKey:@"department.name" ascending:YES];

    NSSortDescriptor *lastNameSort = [NSSortDescriptor sortDescriptorWithKey:@"lastName" ascending:YES];

    [request setSortDescriptors:@[departmentSort, lastNameSort]];

    NSManagedObjectContext *moc = [[self dataController] managedObjectContext];

    [self setFetchedResultsController:[[NSFetchedResultsController alloc] initWithFetchRequest:request managedObjectContext:moc sectionNameKeyPath:@"department.name" cacheName:nil]];

    [[self fetchedResultsController] setDelegate:self];

SWIFT

func initializeFetchedResultsController() {

    let request = NSFetchRequest(entityName: "Person")

    let departmentSort = NSSortDescriptor(key: "department.name", ascending: true)

    let lastNameSort = NSSortDescriptor(key: "lastName", ascending: true)

    request.sortDescriptors = [departmentSort, lastNameSort]

    let moc = dataController.managedObjectContext

    fetchedResultsController = NSFetchedResultsController(fetchRequest: request, managedObjectContext: moc, sectionNameKeyPath: "department.name", cacheName: nil)

    fetchedResultsController.delegate = self

    do {

        try fetchedResultsController.performFetch()

    } catch {

        fatalError("Failed to initialize FetchedResultsController: \(error)")

    }

}

In this example you add one more NSSortDescriptor instances to the NSFetchRequest instance. You set the same key from that new sort descriptor as the sectionNameKeyPath on the initialization of the NSFetchedResultsController. The fetched results controller uses this initial sort controller to break apart the data into the sections and therefore requires that the keys match.

在这个例子中你添加一个更多NSSortDescriptor实例到NSFetchRequest实例。你设置相同的和新集合描述相同的key在sectionNameKeyPath 在NSFetchedResultsController的时候。这个获取结果控制器使用这个初始化集合控制器去去吧数据区分到组中所以需要key到匹配。


This change causes the fetched results controller to break the returning Person instances into multiple sections based on the name of the department that each Person instance is associated with. The only conditions of using this feature are:

这个改变造成获取结果控制器去把返回Person 实例基于Person 实例相关的部分的名字分开到很多组中。使用这个特征的唯一状况是:

The sectionNameKeyPath property must also be an NSSortDescriptor instance. 

 这个sectionNameKeyPath属性必须也是一个NSSortDescriptor实例。


The NSSortDescriptor must be the first descriptor in the array passed to the fetch request.

这个NSSortDescriptor必须是在传递给获取请求数组中的第一个描述。








Adding Caching for Performance

为执行添加高速缓存

In many situations, a table view will represent a relatively static type of data. A fetch request is defined at the creation of the table view controller, and it never changes throughout the life of the application. In those situations it can be advantageous to add a cache to the NSFetchedResultsController instance so that when the application is launched again and the data has not changed, the table view will initialize instantaneously. A cache is especially useful for displaying unusually large data sets.

在很多情景中,一个table view  将展示一个相关的静态类型数据。一个获取请求被定义在tableViewController的创建,和永不改变在应用的生命中。在哪些场景中去添加一个高速缓存到NSFetchedResultsController实例是有利的所以当应用再一次启动数据还没有改变,这个table view将立即初始化。一个高速缓存在展示非常见的大数据集合非常有用。

OBJECTIVE-C

[self setFetchedResultsController:[[NSFetchedResultsController alloc] initWithFetchRequest:request managedObjectContext:moc sectionNameKeyPath:@"department.name" cacheName:@"rootCache"]];

SWIFT

fetchedResultsController = NSFetchedResultsController(fetchRequest: request, managedObjectContext: moc, sectionNameKeyPath: "department.name", cacheName: "rootCache")

As shown above, the cacheName property is set when the NSFetchedResultsController instance is initialized, and the fetched results controller automatically gains a cache. Subsequent loading of the data will be nearly instantaneous.

像上方展示的,这个cacheName属性是一个集合 当NSFetchedResultsController实例被初始化,和这个获取结果控制器自动获得一个高速缓存。随后数据的加载几乎是瞬间的。

NOTE

If a situation occurs where the fetch request associated with a fetched results controller needs to change, then it is vital that the cache be invalidated prior to changing the fetched results controller. You invalidate the cache by calling deleteCacheWithName:, which is a class level method on NSFetchedResultsController.

如果一个场景发生 获取请求使用一个获取结果控制器需要改变,高速缓存不可用在改变获取结果控制器之前是致命重要的,你通过调用deleteCacheWithName:不可用高速缓存,这是NSFetchedResultsController 的一个类方法。







Integrating Core Data at iOS Startup

在iOS启动整合CD

The beginning of an application life cycle is subtly different in iOS and OS X.

When an OS X application takes an unusually long time to launch and becomes unresponsive, the operating system changes the cursor to indicate this state. The user can then decide to wait for the application to finish launching or shut down the application.

一个应用生命周期的开始是轻微不通的在iOS和 OS X。当一个OS X话费一个非正常的长时间去启动和变的无响应,操作系统改变光标去显示这个状态。然后用户决定去等待应用去完成启动或关闭这个应用。


In iOS, there is no such concept. If an app does not launch within a finite amount of time, the operating system terminates the application. Therefore, it is vital that an application complete the startup procedure as quickly as possible.

在iOS,没有这样的概念。如果一个应用不在一个限定时间内启动,这个操作系统终止这个应用。因此,一个应用尽快的完成启动过程是恨关键的。


On the other hand, you want your application to be able to access data inside of Core Data as quickly as possible, which usually means initializing Core Data as one of the first steps in the application’s life cycle. Although atypical, Core Data occasionally takes longer than usual to complete its initialization.

Therefore, it is recommended that the startup sequence of an iOS app be broken into two phases, to avoid termination:

另一个方面,你想你的应用去尽快的访问在CD中的数据,这通常意味着初始化CD作为应用生命周期的第一步。尽管非典型的,CD偶尔花费比平时更长的时间去完成它的初始化。因此,iOS启动序列被分成两段,去避免终止:

1 A minimal startup that indicates to the user that the application is launching 

一个最小启动暗示用户应用正在被启动


2 Complete loading of the application UI once Core Data has completed its initialization 

一旦CD已经完成它的初始化完成应用的UI加载。


Initializing Core Data in iOS

The first step is to change how the application:didFinishLaunchingWithOptions: method is implemented. In the application:didFinishLaunchingWithOptions: method consider initializing Core Data and doing very little else. If you are using a storyboard, you can continue to display the launch image during this method.

第一个是去改变 application:didFinishLaunchingWithOptions:方式如何被实现。在 application:didFinishLaunchingWithOptions:方法考虑初始化CD和做一点。如果你使用一个storyboard,你能继续去展示在这个方法期间去展示启动图片。


As part of the initialization of Core Data, assign the adding of the persistent store (NSPersistentStore) to the persistent store coordinator (NSPersistentStoreCoordinator) to a background queue. That action can take an unknown amount of time, and performing it on the main queue can block the user interface, possibly causing the application to terminate.

作为CD的初始化的一部分,指定持久存储(NSPersistentStore)的添加到持久存储协调器(NSPersistentStoreCoordinator)在一个后台队列。这个动作能花费一个位置数量的时间,和在主队列上能阻塞用户界面,可能造成应用的终结。


Once the persistent store has been added to the persistent store coordinator, you can then call back onto the main queue and request that the user interface be completed and displayed to the user.

一旦持久存储已久被添加到了持久存储协调器上,你能回调到主线程和请求用户界面被完成和展示给用户。

Separating Core Data from the Application Delegate

从应用代理中分开CD

Previously in iOS, the Core Data stack was typically initialized within the application delegate. However, doing so causes a significant amount of code to get muddled in with application life cycle events.

以前在iOS中,这个CD栈被典型初始化在应用代理中。然而这样做导致很多的代码在应用生命循环事件中搞得混乱。





It is recommended that the Core Data stack be created within its own top-level controller object and that the application delegate initialize that controller object and hold a reference to it. This action will promote the consolidation of the Core Data code within its own controller and keep the application delegate relatively clean. This isolated controller design is shown in detail in Initializing the Core Data Stack.

建议CD栈被创建在它自己顶层控制器对象内和应用代理初始化那个控制器对象和有一个引用指向它。这个动作将提高CD代码在它自己控制器的整合和保持应用代理相对干净。这个被分离控制器设计被展示在initializing the Core Data Stack.

To integrate the code from the Initializing the Core Data Stack section into an iOS app, add a property to the application delegate and initialize the controller in the applicationDidFinishLaunching lifecycle method:

去整合代码从Initializing the Core Data Stack 组到一个iOS app.添加一个属性到应用代理和初始化这个控制器在applicationDidFinishLaunching生命循环方法中。

OBJECTIVE-C

@interface AppDelegate : UIResponder <UIApplicationDelegate>

 

@property (strong, nonatomic) UIWindow *window;

@property (strong, nonatomic) DataController *dataController;

 

@end

 

@implementation AppDelegate

 

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions

{

    [self setDataController:[[DataController alloc] init];

    // Basic User Interface initialization

    return YES;

}

 

@end

SWIFT

class AppDelegate a href="" UIResponder /a a href="" UIApplicationDelegate /a  {

   

    var window a href="" UIWindow /a ?

    var dataController a href="" DataController /a !

   

    func application(application a href="" UIApplication /a , didFinishLaunchingWithOptions launchOptions: [ a href="" NSObject /a a href="" AnyObject /a ]?) ->  a href="" Bool /a  {

        dataController = DataController()

        // Basic User Interface initialization

        return true

}


By initializing a separate controller object with a completion block, you have moved the Core Data stack out of the application delegate, but you still allow a callback to the application delegate so that the user interface can know when to begin requesting data.

通过初始化一个分离控制器对象在一个完成闭包中。你已经移动CD栈到了应用代理之外,但是你依旧运行一个方法会调到应用代理以至于用户界面能知道什么时候开始请求数据。








Integrating Core Data and Storyboards

整合CD和Storyboards

Core Data integrates well with Xcode’s Storyboards feature, which you use to build your UI. This integration allows you to take advantage of the dependency injection pattern. Dependency injection is an inversion of control; it allows the caller of a framework to control the flow by passing references into the called object. Dependency Injection is one of the preferred patterns to be used with Cocoa development and specifically with iOS Cocoa development.

CD和Xcode’s的Storyboards特征整合很好,你能用来搭建你的UI。这个整合允许你去利用依赖注入模式。依赖注入是一个控制的反演;它允许框架的调用者去通过传入引用到被调用的对象来控制流向。依赖注入是被Cocoa开发者和特别是使用iOSCocoa开发者的一个便好的模式。







Integrating Core Data with a Storyboard Segue

使用一个Storyboard Segue 整合CD


A complex integration point for Core Data and storyboards is the transition between a table view displaying numerous data objects and a child view controller that presents the details of one of those items. Without using a storyboard, you accomplish this by overriding the UITableViewDelegate method tableView:didSelectRowAtIndexPath:. However, with a storyboard, this method should not be used, and the transition should be handled within the prepareForSegue:sender: method.

对CD和storyboards一个复杂整合点是转换在一个正在展示数据对象的 table view 和一个展示哪些项目中某一个细节的子view控制器。如果不使用storyboard,你通过从写UITableViewDelegate方法tableView:didSelectRowAtIndexPath:.然而,使用这个storyboard,这个方法不应该被使用,和转换应该被处理在prepareForSegue:sender:方法。


To demonstrate how you can integrate Core Data with a storyboard segue, consider the following example, which shows a primary view controller that is a table view with a list of employees. When one of those employees in that list is selected you will want to present a detail view of that employee. It is assumed that the view controller within the segue has a property to receive the selected NSManagedObject.

去证明你如何使用一个storyboard segue去整合CD,考虑下面的例子,它展示一个原始view 控制器是一个table view有一个雇员的列表。当雇员中的一个在那个列表被选择你将想去展示一个雇员的细节View。假设那个在segue中的view 控制器有一个属性去接受被选择的NSManagedObject.



OBJECTIVE-C

@interface DetailViewController : UIViewController

 

@property (weak) AAAEmployeeMO *employee;

 

@end

SWIFT

class DetailViewController a href="" UIViewController /a  {

   

    weak var employee a href="" AAAEmployeeMO /a ?

   

}

Next you implement the prepareForSegue:sender: method in the primary view controller to pass the appropriate NSManagedObject instance during the segue:

接着你实现prepareForSegue:sender:方法在主视图控制器中去传递适当的NSManagedObject 实例在segue期间:

OBJECTIVE-C

#define CellDetailIdentifier @"CellDetailIdentifier"

- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender

{

    id destination = [segue destinationViewController];

    if ([[segue identifier] isEqualToString:CellDetailIdentifier]) {

        NSIndexPath *indexPath = [[self tableView] indexPathForSelectedRow];

        id selectedObject = [[self fetchedResultsController] objectAtIndexPath:indexPath];

        [destination setEmployee:selectedObject];

        return;

    }

}

SWIFT

let CellDetailIdentifier = "CellDetailIdentifier"

override func prepareForSegue(segue a href="" UIStoryboardSegue /a , sender a href="" AnyObject /a ?) {

    switch segue.identifier! {

    case CellDetailIdentifier:

        let destination = segue.destinationViewController as a href="" DetailViewController /a 

        let indexPath = tableView.indexPathForSelectedRow!

        let selectedObject = fetchedResultsController.objectAtIndexPath(indexPath) as a href="" AAAEmployeeMO /a 

        destination.employee = selectedObject

    default:

        print("Unknown segue: \(segue.identifier)")

    }

}










Once the segue identifier (which is required to be unique per segue in a storyboard) is retrieved from the segue, you can safely assume what type of class the destination view controller is, and pass a reference to the selected Employee instance to the destination view controller. The destination view controller will then have the reference available to it during the loading portion of its life cycle and display the data associated with it. This is dependency injection in that the parent view controller is controlling the flow of the application by deciding which Employee instance to hand off to the destination view controller.

一旦segue ID(ID被要求在一个storyboard 中 每一个segue必须是唯一的)被从segue中获取,你能安全的呈现目的控制器是什么类型,和传递一个到被选择雇员实例的引用到目的控制器。这个目的控制器View然后持有那个对他有用的引用在它的生命循环加载期间和展示与之相关的数据。这个依赖注入在父类view 控制器通过决定那个雇员实例被传递到目的view控制器 来 控制应用的流向。










Managed Objects and References

管理对象和引用


References determine when managed objects are released from memory and also affect what causes them to be retained.

引用决定当管理对象被从内存中是否和页影响什么造成它们被保持


Weak References Between Managed Objects and the Context

弱引用在管理对象和上下文之间




Managed objects know what managed object context they’re associated with, and managed object contexts know what managed objects they contain. By default, though, the references between a managed object and its context are weak. This means that in general you cannot rely on a context to ensure the longevity of a managed object instance, and you cannot rely on the existence of a managed object to ensure the longevity of a context. Put another way, just because you fetched an object doesn’t mean it will stay around.

管理对象知道知道它们被联系到什么管理对象上下文上,管理对象上下文页知道它们保持什么管理对象。通过默认,尽管,在管理对象和它的上下文之间的引用是弱引用。这意味通常来说***你不能依赖一个上下文去保证一个管理对象实例的持久***,和你不能依赖一个管理对象的存在去保证一个上下文的持久。换句话说,你获取一个对象不意外这它将停留。


The exception to this rule is that a managed object context maintains a strong reference to any changed (inserted, deleted, and updated) objects until the pending transaction is committed (with a save:) or discarded (with a reset or rollback). Note that the undo manager may also keep strong references to changed objects—see Change Management.

这个规则的意外是***一个管理对象上下文保持一个强引用到任何改变(插入,删除,和更新)对象直到待定的转换被提交(使用 save:)或放弃(使用reset 或 rollback).注意撤销管理器可能也保持强引用到改变对象-参见 Change Management***.


Creating a Strong Reference Between Managed Objects and the Context

在管理对象和上下文之间创建一个强引用


You can change a context’s default behavior so that it does keep strong references to its managed objects by setting the retainsRegisteredObjects property to true. Doing so makes the life of the managed objects dependent on the life of the context. This dependence can be convenient if you are caching smaller data sets in memory. For example, suppose the context controls a temporary set of objects that may persist beyond a single event cycle, such as a sheet in an OS X application or a modal view in an iOS application. Making managed objects dependent on the context can also be useful if you are using multiple threads and passing data between them — for example, if you are performing a background fetch and passing object IDs to the main thread. The background thread needs to keep strong references to the objects it prefetched for the main thread until it knows the main thread has actually used the object IDs to fault local instances into the main thread. Otherwise the objects may fall out of memory and need to be retrieved again from disk.

你能通过设置retainsRegisteredObjects属性为true改变一个上下文的默认行为以至于它保持一个强引用到它的管理对象.这样做管理对象的生命依赖在上下文的生命上。这个依赖非常方便如果你正在缓冲少量的数据在内存中。比如,假设上下文控制一个临时的可能保持在一个单独事件循环之外的对象集合,比如一个sheet在一个OS X应用中或一个modal view在一个iOS应用中。让管理对象依赖在上下文也很有用如果你使用很多线程和在它们之间传递数据-比如,你正在执行一个后台请求和传递对象的IDs到主线程。这个后台线程需要去保存强引用到那个它为主线程预取的对象直到它知道主线程已经真正的使用对象IDs去错误当地实例在主线程中。否则对象们可能从内存中废弃需要去被再次从磁盘中获取。


Use a separate container to keep strong references only to those managed objects you really need. You can use an array or dictionary, or an object controller (for example, an NSArrayController instance) that has strong references to the objects it manages. The managed objects you don’t need will then be deallocated when possible, such as when relationships are cleared (see Breaking Strong References Between Objects).

使用一个分开容器去保持一个强引用仅到哪些你真正需要的管理对象。你能使用一个数组或字典,或一个对象控制器(比如,一个NSArrayController 实例)它有强引用到它管理的对象。这个管理对象你不需要 将在可能的时候被释放,比如当关系被清除的时候(参见Breaking Strong References Between Objects).


If you have finished with a managed object context, or for some other reason you want to disconnect a context from its persistent store coordinator, do not set the context’s coordinator to nil.

Instead, simply relinquish ownership of the context and allow it to be deallocated normally.

***如果你已经完成了使用一个管理对象上下文,或因为其它原因你想去解连接一个上下文从它的持久存储协调器中,不要设置上下文的存储协调器为nil.

而是,简单放弃上下文的所有权和允许它被正常的释放***。


OBJECTIVE-C

[self setMyManagedObjectContext:nil];

SWIFT

myManagedObjectContext = nil



Breaking Strong References Between Objects

打破对象之间的强引用

As opposed to the default behavior between managed objects and their managed object contexts, with relationships between managed objects, each object maintains a strong reference to the object or objects to which it is related. This relationship can cause strong reference cycles which in turn can cause objects to be held in memory long past their usefulness. To ensure that reference cycles are broken, when you are finished with an object, you can use the managed object context method refreshObject:mergeChanges: to turn the managed object into a fault.

作为和管理对象之间以及管理对象和上下文之间行为相反的,使用管理对象之间的管理,每个对象保持一个强引用到和它相联系的一个对象或多个对象。这个关系能造成强引用循环这能造成对象被保持在内存中尽管已经没用。去确保那个引用循环被打破,当你使用完一个对象,你能使用管理对象上下文方法refreshObject:mergeChanges:把这个管理对象转到

You typically use refreshObject:mergeChanges: to refresh a managed object’s property values. If the mergeChanges flag is YES, the method merges the object’s property values with those of the object available in the persistent store coordinator. If the flag is NO, however, the method simply turns an object back into a fault without merging, which causes it to break strong references to related managed objects.

你通常使用refreshObject:mergeChanges:去更新一个管理对象的属性值。如果mergeChanges标示是YES,这个方法融合那个对象的属性值和哪些对象的可用的在持久存储协调器中的。如果标示是NO,那样这个方法只是把一个对象转回到一个faul而没有融合,这造成它去打破联系到管理对象的强引用。


Before a managed object can be deallocated, any strong references to it must be removed, including those from outside of Core Data. See also Change Management.

在一个管理对象能被释放前,任何到它的强引用必须被移除,保护哪些在CD之外的。参见Change Management.









Strong References in Change and Undo Management

强引用在改变和撤销管理

A context keeps strong references to managed objects that have pending changes (insertions, deletions, or updates) until the context is sent a save:, reset , rollback, or dealloc call, or the context is sent the appropriate number of undos to undo the change. If the calls to undo cause all changes to be undone on a particular object, the object’s strong reference will revert back to a weak reference.

一个上下文保持强引用到有未决定的改变(插入,删除,或更新)管理对象知道这个上下文被发送一个save:,reset,rollback,或dealloc调用,或这个上下文被发送了合适数量的撤销去撤销哪些改变。如果调用撤销造成所有改变到被撤销 在一个特殊对象,这个对象的强引用将被回滚到一个弱引用。


The undo manager associated with a context keeps strong references to any changed managed objects. By default, the context’s undo manager keeps an unlimited undo and redo stack. To limit your application's memory footprint, make sure that you scrub (using removeAllActions) the context’s undo stack when appropriate.

这个撤销管理者 和一个保持强引用到任何改变对象上下文 联系起来。默认情况,这个上下文的撤销管理者保持一个无限的撤销和重做栈。去限制你的应用的内存占用,确保你清除(使用removeAllActions)上下文的撤销栈在合适的时候。


If you do not intend to use Core Data’s undo functionality, you can reduce your application's resource requirements by setting the context’s undo manager to nil. This may be especially beneficial for background worker threads, as well as for large import or batch operations.

如果你不打算去用CD的撤销功能,你能通过设置上下文的撤销管理者为nil去减少你应用的资源需求。这个可能对后退工作线程很有利,同时对大量的输入和批操作也有利。











Ensuring Data Is Up to Date

确保数据是最新的

If two applications are using the same data store, or a single application has multiple persistence stacks, it is possible for managed objects in one managed object context or persistent object store to get out of sync with the contents of the repository. If this occurs, you need to refresh the data in the managed objects, and in particular the persistent object store (the snapshots) to ensure that the data values are current.

如果两个应用在使用相同数据存储,或一个单独的应用有或多 持久栈,管理对象在一个管理对象上下文或持久对象存储和仓库的内容不是同步的。如果这个发生,你需要去刷新数据在管理对象中,和特别的这个持久对象存储(这个快照)去保证数据之是当前的。


Refreshing an Object

刷新一个对象

Managed objects whose property values are populated from the persistent store (realized objects), as well as pending updated, inserted, or deleted objects, are never changed by a fetch operation without developer intervention. For example, consider a scenario in which you fetch some objects and modify them in one editing context; meanwhile, in another editing context you edit the same data and commit the changes. If in the first editing context you then execute a new fetch that returns the same objects, you do not see the newly committed data values—you see the existing objects in their current in-memory state.

管理对象它的属性值是从持久存储(真实对象)产生的,还有未决定的更新,插入,或删除对象,都不会通过一个获取操作改变除非开发者干涉。比如,考虑一个剧情你获取一些对象和修改它们在一个编辑上下文;同时在另一个编辑上下文你编辑同样的数据和提交哪些改变。如果在第一编辑上下文你然后执行一个新的返回相同数据获取,你没有看到最新的被提交数据的值-你看到的正在存在对象在它们当前在内存中的状态。


To refresh a managed object's property values, you use the managed object context method refreshObject:mergeChanges:. If the mergeChanges flag is YES, the method merges the object's property values with those of the object available in the persistent store coordinator. If the flag is set to NO, the method simply turns an object back into a fault without merging, which also causes strong references to other related managed objects to be broken. Thus you can use this method to trim the portion of your object graph that you want to hold in memory.

去刷新一个管理对象的属性值,你使用管理对象上下方法refreshObject:mergeChanges:.如果mergeChanges标示是YES,这个方法合并对象的属性值和在哪些可用的持久存储协调器中对象。如果标示是NO,这个方法这个方法仅仅转化对象到回到fault 而没有合并,这也会造成到其它相关管理对象 强引用 被打破。也就是你能使用这个方法去修剪部分你想去在保持在内存中的对象图的部分。


An object’s staleness interval is the time that has to pass until the store refetches the snapshot. The staleness interval only affects firing faults; moreover, it is only relevant for incremental (i.e. SQLite) stores. The other stores never refetch because the entire data set is kept in memory.

一个对象的失效间隔是已经传递到直到存储再获取快照。这个失效间隔只影响触发错误;它只和增量(即SQLite)存储相关其它存储永不悔重新获取因为整个数据集合被保存在内存中。


Merging Changes with Transient Properties

使用暂时变量合并改变

A transient property is a property on an object that is not persisted to disk. If you use refreshObject:mergeChanges: with the mergeChanges flag YES, then any transient properties are restored to their prerefresh value after awakeFromFetch is invoked. This means that, if you have a transient property with a value that depends on a property that is refreshed, the transient value may become out of sync.

一个临时属性是一个在对象上的不持久化到磁盘的属性。如果你使用refreshObject:mergeChanges:使用mergeChanges标示为YES,然后任何暂时属性被恢复到它们的上一个刷新值在awakeFromFetch 之后。这就意外着如果你有一个暂时属性 属性 有一个取决于一个被刷新属性的值,这个暂时值可能不是同步的。


Consider an application in which you have a Person entity with attributes firstName and lastName, and a cached transient derived property, fullName. (In practice it might be unlikely that a fullName attribute would be cached, but the example is easy to understand.) Suppose also that fullName is calculated and cached in a custom awakeFromFetch method.

考虑一个应用中你有一个 人 实体 有属性firstName 和 lastName,和一个缓存暂时衍生属性,fullName.(在实际中它可能不想一个fullName属性将被缓存,但是这个例子更好去理解。)假设这个fullName是被计算和缓存在一个自定义的awakeFromFetch方法中。


An Employee, currently named "Sarit Smith" in the persistent store, is edited in two managed object contexts:

一个雇员,当前名字是“Sarit Smith”在一个持久存储,被在两个管理对象上下文中编辑:

In context one, the corresponding instance's firstName is changed to "Fiona", which causes the cached fullName to be updated to "Fiona Smith", and the context is saved. 

在上下文一,这个对应实例的fristName被改变到“Fiona”,这造成被缓存的fullName被更新成“Fiona Smith”和上下文被保存。


In the persistent store, the employee is now “Fiona Smith”. 

In context two, the corresponding instance's lastName is changed to "Jones", which causes the cached fullName to be updated to "Sarit Jones". 

在持久存储,这个雇员现在是“Fiona Smith”.


在上下文2,这个相应实例的lastName被改变到“Jones”,这就造成被缓存fullName被更新到“Sarit Jones”.


The object is then refreshed with the
mergeChanges flag YES. The refresh fetches “Fiona Smith” from the store. The refresh updates the object as follows: 

这个对象然后使用mergeChanges标示YES刷新。这个刷新获取“Fiona Smith”从存储中。这个刷新 更新对象是如下的:

firstName was not changed prior to the refresh; the refresh causes it to be updated to the new value from the persistent store, so it is now "Fiona". 

在刷新之前firstName没有改变;这个刷新导致它被更新到从持久存储来的最新值,所以它现在是“Fiona”.


lastNamewas changed prior to the refresh; so, after the refresh, it is set back to its modified value—"Jones". 

在刷新之前lastName被改变;所以刷新以后,所以它被设置回它的改变值-“Jones”.


The transient value, fullName, was also changed prior to the refresh. After the refresh, its value is restored to "Sarit Jones". However, to be correct, it should be "Fiona Jones". 

这个暂时值,fullName,也是在刷新之前被改变。在刷新之后,它的值被恢复为“Sarit Jones”.然而,正确的是,它应该是“Fiona Jones”.






The example shows that, because prerefresh values are applied after awakeFromFetch, you cannot use awakeFromFetch to ensure that a transient value is properly updated following a refresh (or if you do, the value will subsequently be overwritten). In these circumstances, the best solution is to use an additional instance variable to note that a refresh has occurred and that the transient value should be recalculated.

这个例子展示,因为预刷新值被应用在awakeFromFetch之后,你不能使用awakeFromFetch去保证一个暂时值被合适更新在一个刷新以后(或如果你这样做,这个值将在随后被覆盖)。在哪些环境中,这个最后的结局是去使用一个另外的实例变化的去通知一个刷新已经发生和这个暂时属性应该被重新计算。





Creating Managed Object Relationships

创建管理对象之间的关系


A managed object is associated with an entity description (an instance of NSEntityDescription) that provides metadata about the object and with a managed object context that tracks changes to the object graph. The object metadata includes the name of the entity that the object represents and the names of its attributes and relationships.

一个管理对象是和一个 提供关于对象和一个追寻到对象图表改变的管理对象上下文元数据 的实体描述(一个NSEntityDescription实例)相联系的。这个对象元数据包含 对象所呈现的他的属性和关系的名字实体的名字。

 

In a given managed object context, a managed object provides a representation of a record in a persistent store. In a given context, for a given record in a persistent store, there can be only one corresponding managed object, but there may be multiple contexts, each containing a separate managed object representing that record. Put another way, there is a to-one relationship between a managed object and the data record it represents, but a to-many relationship between the record and corresponding managed objects.

在一个给定的管理对象上下文,一个管理对象提供一个记录的展示在持久存储中。在一个给定上下文,因为一个在持久存储中的给定记录,只能有一个对应管理对象,但是可能有很多上下文,每个包含一个分开的管理对象呈现那个记录。换一个方式,有一对一的关系在在管理对象和它呈现的数据记录之间,但是对多关系


NOTE

Core Data does not let you create relationships that cross stores. If you need to create a relationship from objects in one store to objects in another, consider using Weak Relationships (Fetched Properties).

CD不允许你创建跨越存储的关系。如果你需要去创建一个关系从一个存储到另一个存储中的对象,考虑使用Weak RelationShips(Fetched Properties).


Relationship Definitions in the Managed Object Model

在管理对象模型中定义关系

There are a number of things you have to decide when you create a relationship. What is the destination entity? Is the relationship a to-one or a to-many? Is the relationship optional? If it’s a to-many, are there maximum or minimum numbers of objects that can be in the relationship? What should happen when the source object is deleted?

当你创建一个关系有很多你不得不去决定的事。目的实体是什么?关系是一对一或一对多?关系是否是可选的?是否有最大或最小能在关系之中的对象数量?当源对象被删除的时候发生什么?


In an object model relationship, you have a source entity (for example, Department) and a destination entity (for example, Employee). You define a relationship between a source entity and a destination entity in the Relationship panel of the Data Model inspector for the source entity.

在一个对象模型关系,你有一个源实体(比如,部分)和一个目的实体(比如,雇员)。你定义一个关系为在源实体在源实体和一个目的实体之间在数据模型检查器的关系窗格中。


Figure 12-1Relationship pane in the Data Model inspector

 


Relationship Fundamentals

关系的基础

A relationship specifies the entity, or the parent entity, of the objects at the destination. This entity can be the same as the entity at the source (a reflexive relationship). Relationships do not have to reference a single entity type. If the Employee entity has two subentities, say Manager and Assistant, and Employee is not abstract, then a given department's employees may be made up of Employees (the primary entity), Managers (subentity of Employee), Assistants (subentity of Employee), or any combination thereof.

In the Type field of the Relationship pane, you can specify a relationship as being to-one or to-many, which is known as its cardinality.

一个关系明确在目的的对象的实体,或父类实体。这个实体能和在源(一个自反关系)中的实体是相同的。关系不顺不得不引用到一个单独实体类型。如果雇员类型有两个子实体,经理和伙计,和雇员不少一个抽象,然后一个给定部门的雇员啃呢个由雇员构成(原始实体),经理(雇员的子实体),伙计(雇员的子实体),或任何组合。在关系窗格的类型区域,你能明确一个关系是一对一或一对多,也就是它的基数。


 To-one relationships are represented by a reference to the destination object and to-many relationships are represented by mutable sets. Implicitly, to-one and to-many typically refer to one-to-one and one-to-many relationships respectively. A many-to-many relationship is one where both a relationship and its inverse are to-many. How you model a many-to-many relationship depends on the semantics of your schema. For details on this type of relationship, see Many-to-Many Relationships.

对一关系被通过一个到目的对象的引用呈现和对多关系被通过可变集合呈现。隐式的,对一 和 对多 分别就是说 一对一 和 一对多 关系。一个多对多关系是一个它的关系和它的可逆是对多的。你怎么模型化一个多对多关系由你图表的语义决定的。更多这个关系的类型的细节,参见Many-to-Many Relationships.


You can also put upper and lower limits on the number of objects at the destination of a to-many relationship. The lower limit does not have to be zero. You can specify that the number of employees in a department must be between 3 and 40. You also specify a relationship as either optional or not optional. If a relationship is not optional, then for it to be valid there must be an object or objects at the destination of the relationship.

你也能创建一个对多关系的目的上的对象的最大或最小数量的限制。这个最小限制不是必须是零。你能明确在一个部门中雇员的数量必须在3到40之间。你也明确一个关系是可选的或不是可选的。如果一个关系不是可选的,如果一个关系不是可选的,然后为了让他被可用在关系的目的上必须有一个或多个对象。



Cardinality and optionality are orthogonal properties of a relationship. You can specify that a relationship is optional, even if you have specified upper and/or lower bounds. This means that there do not have to be any objects at the destination, but if there are, then the number of objects must lie within the bounds specified.

基数和可选性是一个关系的正交属性。你能明确一个关系是可选的。就算你明确了上边界和下边界。这意味着在目的不是必有任何对象,但是如果有对象,对象的数量必须在边界明确的范围内。



Creating Relationships Does Not Create Objects

创建关系不创建对象

It is important to note that simply defining a relationship does not cause a destination object to be created when a new source object is created. In this respect, defining a relationship is akin to declaring an instance variable in a standard Objective-C class. Consider the following example.

简单定义一个关系不会造成一个目的对象被创建当一个新源对象被创建。在这个方面,定义一个关系是和声明一个实例变量在一个标准Objective-C类是同源的。考虑下面的例子。

OBJECTIVE-C

@interface AAAEmployeeMO : NSManagedObject

 

    @property (nonatomic, strong) AAAAddressMO *address;

 

@end

SWIFT

class AAAEmployeeMO a href="" NSManagedObject /a  {

    @NSManaged address: AAAAddressMO?

}

If you create an instance of Employee, an instance of Address is not created unless you write code to cause it to happen. Similarly, if you define an Address entity, and a non-optional to-one relationship from Employee to Address, then simply creating an instance of Employee does not create a new Address instance. Likewise, if you define a nonoptional to-many relationship from Employee to Address with a minimum count of 1, then simply creating an instance of Employee does not create a new Address instance.

如果你创一个雇员的实例,一个地址实例没有被创建除非你写代码去让它发生。相似的,如果你定义了一个地址实体,和一个非可选的对一关系 从雇员到地震,然后简单创建一个雇员实例不创建一个新地址实例。同样,如果使用最小数量1你定义一个没有选择的对多关系从雇员到地址,然后简单创建雇员的实例不创建一个新地址实例。


Inverse Relationships

反转关系

Most object relationships are inherently bidirectional. If a Department has a to-many relationship to the Employees who work in a Department, there is an inverse relationship from an Employee to the Department that is to-one. The major exception is a fetched property, which represents a weak one-way relationship—there is no relationship from the destination to the source. See Weak Relationships (Fetched Properties).

很多对象关系是固有双向的。如果一个部门有一个一对多关系到在这个部门工作的雇员,这里有一个反向关系从雇员到部门是对一的。主要的意外是获取属性,那展示一个弱 单路关系-哪里没有从目的到源的关系。参见Weak Relationships(Fetched Properties).


It is highly recommended that you model relationships in both directions, and specify the inverse relationships appropriately. Core Data uses this information to ensure the consistency of the object graph if a change is made (see Manipulating Relationships and Object Graph Integrity).

高度建议你在两个方向模型关系,和合适的明确反向关系。CD使用这个信息去保证对象图的一致性如果一个改变被创建(参见Manipulating Relationships and Object Graph Integrity).


Relationship Delete Rules

关系删除规则

A relationship's delete rule specifies what should happen if an attempt is made to delete the source object. Note the phrasing if an attempt is made. If a relationship's delete rule is set to Deny, it is possible that the source object will not be deleted. Consider again a department's employees relationship, and the effect of the different delete rules.

一个关系的删除规则明确什么应该发生如果一个尝试去删除源对象。注意阶段是尝试被制造。如果一个关系的删除规则被设置到否认,源对象将不被删除是可能的。在次考虑一个部门的雇员关系,和不同删除关系的影响。




Deny

否认

If there is at least one object at the relationship destination (employees), do not delete the source object (department).

For example, if you want to remove a department, you must ensure that all the employees in that department are first transferred elsewhere (or fired!); otherwise, the department cannot be deleted.

如果最少有至少一个对象在关系目的(雇员),不要删除源对象(部门)。

比如,如果你想去一个部门,你必须确保所有在部门的雇员被转移到其它地方(或除名);否则这个部门不能被删除。


Nullify

注销

Remove the relationship between the objects but do not delete either object.

This only makes sense if the department relationship for an employee is optional, or if you ensure that you set a new department for each of the employees before the next save operation.

移除对象之间的关系但是不删除任何对象。这仅仅制造场景如果一个雇员部门关系是可选的,或如果你确保你设置一个新部门为每个雇员在文档被保存之前。


Cascade

及联

Delete the objects at the destination of the relationship when you delete the

source.

For example, if you delete a department, fire all the employees in that department at the same time.

删除在关系的目的上的对象当你删除源的时候。

比如,如果你删除一个部门,清除所有同时清除所有在那个部门的雇员。


No Action

没有行动

Do nothing to the object at the destination of the relationship.

For example, if you delete a department, leave all the employees as they are, even if they still believe they belong to that department.

It should be clear that the first three of these rules are useful in different circumstances. For any given relationship, it is up to you to choose which is most appropriate, depending on the business logic. It is less obvious why the No Action rule might be of use, because if you use it, it is possible to leave the object graph in an inconsistent state (employees having a relationship to a deleted department).

对在关系目的上的对象什么都不做。

比如,如果你删除一个部门,保留所有的雇员,

即使它们已久相信它们属于那个部门。

第一上面前三个规则在不同情况下非常有用。对任何给定的关系,是由你来决定最合适的。由业务逻辑来决定。为甚么No Action可能被使用是不明显的,因为你使用它,可能遗留对象图表在一个不一致状态(雇员有一个关系到一个被删除部门)。


If you use the No Action rule, it is up to you to ensure that the consistency of the object graph is maintained. You are responsible for setting any inverse relationship to a meaningful value. This may be of benefit in a situation where you have a to-many relationship and there may be a large number of objects at the destination.

如果你使用No Action 规则,由你去保证对象图表的一致性被保持。你负责去设置任何反转关系到一个有意义的值。这可能在一个场景中有利就是你有一个 对多关系和在目的上有大数量的对象。



Manipulating Relationships and Object Graph Integrity

操作关系和对象图表的整合

When you modify an object graph, it is important to maintain referential integrity. Core Data makes it easy for you to alter relationships between managed objects without causing referential integrity errors. Much of this behavior derives from the relationship descriptions specified in the managed object model.

当你改变一个对象图表,去保持引用的完整性很重要。CD让你选择管理对象之间的关系而不造成引用完整错误很重要。很多这个行为来自明确在管理对象模型的关系描述。

When you need to change a relationship, Core Data takes care of the object graph consistency maintenance for you, so you need to change only one end of a relationship. This feature applies to to-one, to-many, and many-to-many relationships. Consider the following examples.

当你需要去改变一个关系,CD负责对象图表一致性为你,所以你仅仅需要去改变一个关系的末端。这个特征应用到 对一,对多, 多对多关系,考虑如下例子。


An employee’s relationship to a manager implies an inverse relationship between a manager and the manager’s employees. If a new employee is assigned to a particular manager, it is important that the manager be made aware of this responsibility. The new employee must be added to the manager’s list of reports. Similarly, if an employee is transferred from one department to another, a number of modifications must be made, as illustrated in Figure 12-2. The employee’s new department is set, the employee is removed from the previous department’s list of employees, and the employee is added to the new department’s list of employees.

一个雇员到一个经理的关系是一个反转关系在一个经理和经理的雇员之间。如果一个新雇员被指定到一个特别经理,管理者意识到这个责任是重要的。这个新雇员必须被添加到井里的报告列表上。相似的,如果一个雇员被从一个转移到另一个,必须做出一些修改,就像表12-2阐述的。这个雇员的新部门设置,这个雇员从前一个部门的雇员清单中移除,和这个雇员被添加到新部门的雇员的清单。



Figure 12-2Inverse relationship maintaining integrity

 









Without the Core Data framework, you must write several lines of code to ensure that the consistency of the object graph is maintained. Moreover you must be familiar with the implementation of the Department class to know whether or not the inverse relationship should be set from the employee to the new department. This may change as the application evolves. Using the Core Data framework, all this can be accomplished with a single line of code:

没有CD框架,你必须写几行代码去确保对象图表的一致性被保持。更多的是你必须对部门类的实现熟悉 知道从雇员到新部门是否反转关系应该被设置。这个随着应用的演变可能改变。使用CD框架,所有这些使用一行代码就能使用。


OBJECTIVE-C

anEmployee.department = newDepartment;

SWIFT

anEmployee.department = newDepartment

Alternatively, you can use:

OBJECTIVE-C

[newDepartment addEmployeeObject:anEmployee];

SWIFT

newDepartment.mutableSetValueForKey(“employees").addObject(employee

)

Both of these have the same net effect: By referencing the application’s managed object model, the framework automatically determines from the current state of the object graph which relationships must be established and which must be broken.

哪些有相同的净效应:通过引用应用的管理对象模型,这个框架自动决定从对象图表的当前状态那个关系必须被建立和那个关系被打破。


Many-to-Many Relationships

多对多关系

You define a many-to-many relationship using two to-many relationships. The first to-many relationship goes from the first entity (the source entity) to the second entity (the destination). The second to-many relationship goes from the second entity (the original destination entity) to the first entity (the original source entity). You then set each to be the inverse of the other. (If you have a background in database management and this causes you concern, don't worry: if you use a SQLite store, Core Data automatically creates the intermediate join table for you.)

你使用两个对多关系定义一个多对多关系。第一个对多关系从第一个实体(源实体)到第二个实体(目的)。第二个 对多实体关系从第二个实体(原始目的实体)到第一个实体(原始的源实体)。你然后设置每一个都是另一个的反转。(如果你有背景在数据基础管理中这造成你担心,不用担心,如果你使用一个SQLite存储,CD自动创建中间连接表为你。)


IMPORTANT

You must define many-to-many relationships in both directions—that is, you must specify two relationships, each being the inverse of the other. You can’t just define a to-many relationship in one direction and try to use it as a many-to-many. If you do, you will end up with referential integrity problems.

你必须定义多对多关系在两个方向-也就是你必须明确两个关系,每一个是另一个的反转。你不能定义一个 对多 关系 在一个方向和试图去用它作为一个多对多关系。如果你这样做你将使用参照完整性问题结束。


This relationship configuration works even for relationships where an entity has a relationship back to itself (often called reflexive relationships). For example, if an employee can have more than one manager (and a manager can have more than one direct report), then you can define a to-many relationship directReports from the Employee entity to itself that is the inverse of another to-many relationship, managers, again from the Employee entity back to itself. This is illustrated in Figure 12-3.

这个关系配置工作甚至对一个实体有一个关系到他自己(通常被称作自反关系)关系。比如一个一个雇员有多个经理(和一个经理能有多个直接报告),然后你能定义一个 直接报告从雇员实体到它自己 是另一个 对多关系的反转,管理着再次从雇员实体会到它自己。这个在表12-3中被阐明。
















Figure 12-3Example of a reflexive many-to-many relationship

 


Modeling a Relationship Based on Its Semantics

基于它的语义模型化关系

Consider the semantics of the relationship and how it should be modeled. A common example of a relationship that is initially modeled as a many-to-many relationship that’s the inverse of itself is “friends”. Although you are your cousin’s cousin whether the cousins like it or not, it’s not necessarily the case that you are your friend’s friend. For this sort of relationship, use an intermediate (join) entity. An advantage of the intermediate entity is that you can also use it to add more information to the relationship. For example, a FriendInfo entity might include some indication of the strength of the friendship with a ranking attribute, as shown in Figure 12-4.

考虑关系的语义和它应该被如何模型化。一个常见的一个关系的例子是初始化模型作为一个多对多关系是它自己的朋友的反转。尽管你是你表兄的表兄不管你表兄是否喜欢,那并不是必需的情况你是你的朋友的朋友。对这种关系的类型。使用一个中间实体。中间实体的优点是你也能使用它去添加更多信息到关系中。比如,一个 朋友关系实体 使用一个范围性熟悉 可能包含 友谊的强度的标示,就像表12-4.




Figure 12-4A model illustrating a friends relationship using FriendInfo as an intermediate entity

 

In this example, Person has two to-many relationships to FriendInfo: friends represents the source person’s friends, and befriendedBy represents those who count the source as their friend. FriendInfo represents information about one friendship, in one direction. A given instance notes who the source is, and one person they consider to be their friend. If the feeling is mutual, then there will be a corresponding instance where source and friend are swapped. There are several other considerations when dealing with this sort of model:

在这个例子中,人有两个对多关系到 FriendInfo,朋友关系展示了人的朋友,和befriendedBy 展示了哪些谁把 人作为它们的朋友。朋友信息 展示信息 关于 一个友谊,在一个方向,一个给实例显示了谁是源,和他们认为是他们的朋友的一个人。如果感觉是相互的,然后会有相对应的源和朋友是交换的实例。这里有一些其它需要考虑的当处理这个类型的模型:


To establish a friendship from one person to another, you have to create an instance of FriendInfo. If both people like each other, you have to create two instances of FriendInfo. 

去建立一个关系从一个人到另一个人,你不得不去创建一个FriendInfo的实例。如果两个人互相喜欢,你不得不去创建FriendInfo的两个实例。


To break a friendship, you must delete the appropriate instance of FriendInfo. 

去打破一个友谊,你必须删除相关的FriendInfo的实例。


The delete rule from Person to FriendInfo should be Cascade. That is, if a person is removed from the store, then the FriendInfo instance becomes invalid, so it must also be removed. 

从人到FriendInfo 的删除规则应该被及联。也就是,如果一个人被从存储中删除,然后FriendInfo实例变得无效,所以它也必须被移除。


As a corollary, the relationships from FriendInfo to Person must not be optional—an instance of FriendInfo is invalid if the
source or friend is null. 

作为一个必然结果,这个从FriendInfo 到 人 关系必须不是可选的-一个FrendInfo的实例是不可用的如果source 或friend 为空。


To find out who one person’s friends are, you have to aggregate all the friend destinations of the friends relationship, for example: 

去找到一个人的朋友是谁,你不等不去聚集所有朋友关系的目的朋友,
OBJECTIVE-C

NSSet *personsFriends = [aPerson valueForKeyPath:@"friends.friend"];


SWIFT

let personsFriends = aPerson.valueForKeyPath("friends.friend")



To find out who considers a given person to be their friend, you have to aggregate all the source destinations of the befriendedBy relationship, for example: 

去找到谁认为一个给定的人是他们的朋友,你不得不去聚集所有befriendBy关系的目的 source
OBJECTIVE-C

NSSet *befriendedByPerson = [aPerson valueForKeyPath:@"befriendedBy.source"];


SWIFT

let befriendedByPerson = aPerson.valueForKeyPath("befriendedBy.source")










Cross-Store Relationships Not Supported

跨越存储关系不支持

Be careful not to create relationships from instances in one persistent store to instances in another persistent store, as this is not supported by Core Data. If you need to create a relationship between entities in different stores, you typically use fetched properties. See the next section.

当心不要去创建关系从在一个持久存储中的实例到另一个持久存储中的实例,因为这个不被CD支持。如果你需要去创建一个关系在不同存储实体之间,你可以使用获取属性。看下一组。


Weak Relationships (Fetched Properties)

弱关系(获取属性)

Fetched properties represent weak, one-way relationships. In the employees and departments domain, a fetched property of a department might be Recent Hires. Employees do not have an inverse to the Recent Hires relationship. In general, fetched properties are best suited to modeling cross-store relationships, loosely coupled relationships, and similar transient groupings.

获取属性展示弱的,一个-方式 关系。在雇员和部门范围,一个部门的获取属性可能是 最近雇用 。雇员没有一个到最近雇佣关系的反转。通常来说,获取属性是最适合去模型化跨越存储的关系,松散连接关系,和类似暂时分组。


A fetched property is like a relationship, but it differs in several important ways:

一个获取属性就像一个关系,但是在几个重要的方面是不同的:


Rather than being a direct relationship, a fetched property's value is calculated using a fetch request. (The fetch request typically uses a predicate to constrain the result.) 

而不是一个直径关系,一个获取属性的值是使用获取请求计算的。(这个获取请求使用谓词去约束结果。)


A fetched property is represented by an array (NSArray), not a set (NSSet). The fetch request associated with the property can have a sort ordering, and thus the fetched property may be ordered. 

A fetched property is evaluated lazily, and is subsequently cached. 

一个获取属性被一个数组(NSArray)展示,不是一个集合(NSSet)。这个获取请求相关的属性能有一个集合顺序,获取属性可能被排序。一个获取属性被懒加载,在之后被高速缓存。


Figure 12-5Adding a fetched property to an entity

 

In some respects you can think of a fetched property as being similar to a smart playlist, but with the important constraint that it is not dynamic. If objects in the destination entity are changed, you must reevaluate the fetched property to ensure it is up to date. You use refreshObject:mergeChanges: to manually refresh the properties—this causes the fetch request associated with this property to be executed again when the object fault is next fired.

You can use two special variables in the predicate of a fetched property, $FETCH_SOURCE and $FETCHED_PROPERTY. The source refers to the specific managed object that has this property, and you can create key paths that originate with that source, for example, university.name LIKE [c] $FETCH_SOURCE.searchTerm. The $FETCHED_PROPERTY is the entity's fetched property description. The property description has a userInfo dictionary that you can populate with whatever key-value pairs you want. You can therefore change some expressions within a fetched property's predicate or any object to which that object is related.

在一些方面你能认为获取属性是和小播放列表,但是使用重要限制不是动态的。如果在目的实体的对象被改变,你必须重新评估它的属性去确保它被更新。你使用refreshObject:mergeChanges:去手动刷新属性-这导致和这个属性相联系的获取请求被再次执行当对象故障是下一个被触发。你能使用两个特殊变量在一个获取属性的谓词中,$FETCH_SOURCE和$FETCHED_PROPERTY。这个源指向一个明确的有这个属性的管理对象,和你能创建钥匙路径和源一起发生,比如,university.name LIKE[c] $FETCH_searchTerm.这个$FETCHED_PROPERTY是实体的获取属性描述。这个属性描述有一个用户信息字典 你能用字典填充你相要的任何 键值 对。你能因此改变一些表达在一个获取属性谓词中或任何与那个属性相关的对象。


To understand how the variables work, consider a fetched property with a destination entity Author and a predicate of the form, (university.name LIKE [c] $FETCH_SOURCE.searchTerm) AND (favoriteColor LIKE [c] $FETCHED_PROPERTY.userInfo.color). If the source object had an attribute searchTerm equal to Cambridge, and the fetched property had a user info dictionary with a key color and value Green, then the resulting predicate would be (university.name LIKE [c] "Cambridge") AND (favoriteColor LIKE [c] "Green"). The fetched property would match any Authors at Cambridge whose favorite color is green. If you changed the value of searchTerm in the source object to Durham, then the predicate would be (university.name LIKE [c] "Durham") AND (favoriteColor LIKE [c] “Green").

去明白变量如何工作,考虑一个获取属性使用一个目的实体 作者和一个形式的谓词,(university.name LIKE[c]  $FETCH_SOURCE.searchTerm)AND (favoriteColor LIKE [c] $FETCHED_PROPERTY.userInfo.color).如果源对象有一个属性serchTerm相当于剑桥,和获取属性有一个用户信息字典有一个color key 和一个绿色值,然后结果谓词将是(university.name LIKE [c] “Cambridge”)AND (favoriteColor LIKE [c] “Green”).这个获取属性将匹配任何在剑桥的最喜欢颜色是绿色的作者。如果你改变在源对象中搜索项的值为Durham,然后谓词将变成(university.name LIKE [c] “Durham”) AND (favoriteColor LIKE [c] “Green”).


The most significant constraint for fetched properties is that you cannot use substitutions to change the structure of the predicate — for example, you cannot change a LIKE predicate to a compound predicate, nor can you change the operator (in this example, LIKE [c]).

对获取属性来说最显著限制是你不能使用替换去改变谓词的结构-比如你不能改变一个LIKE谓词到一个复合谓词,你也不能改变操作(在这个例子,LIKE [c]).










Faulting and Uniquing

断层和唯一


Faulting reduces your application’s memory usage by keeping placeholder objects (faults) in the persistent store. A related feature called uniquing ensures that, in a given managed object context, you never have more than one managed object to represent a given record.

断层减少你的应用的内存使用通过保持占位符对象(断层)在持久存储。一个相关特征调用唯一保证它,在一个给定管理对象上下文,你永远不要有超过一个管理对象去呈现一个给定记录。



Faulting Limits the Size of the Object Graph

断层限制对象图表的大小

Managed objects typically represent data held in a persistent store. In some situations a managed object may be a fault — an object whose property values have not yet been loaded from the external data store. Faulting reduces the amount of memory your application consumes. A fault is a placeholder object that represents a managed object that has not yet been fully realized, or a collection object that represents a relationship:

管理对象典型呈现在持久存储中的数据。在一些情景一个管理对象可能是一个断层- 一个对象它的属性值还没有被宠额外数据存储中加载。一个断层是一呈现一个还没有被完全实际化的管理对象占位符对象,或展示一个关系的集合对象:

A managed object fault is an instance of the appropriate class, but its persistent variables are not yet initialized. 

一个管理对象断层是一个相关类的实例,但是它的持久变量还没有初始化。


A relationship fault is a subclass of the collection class that represents the relationship. 

一个关系断层是呈现关系的集合类的之类。






Faulting allows Core Data to put boundaries on the object graph. Because a fault is not realized, a managed object fault consumes less memory, and managed objects related to a fault are not required to be represented in memory at all.

断层允许CD去放置边界在对象图表上。因为一个断层没有被实际化。一个管理对象断层消耗更少内存,联系到断层的管理对象不要求被在内存中完全呈现。

To illustrate, consider an application that allows a user to fetch and edit details about a single employee. The employee has a relationship to a manager and to a department, and these objects in turn have other relationships. If you retrieve just a single Employee object from a persistent store, its manager, department, and reports relationships are initially represented by faults. Figure 13-1 shows an employee’s department relationship represented by a fault.

去阐明,考虑一个允许一个用户去获取和编辑关于一个单独雇员细节的应用。这个用户有一个关系到一个经理和到一个部门,和哪些对象反过来有其他关系,如果你获取仅仅一个单独雇员对象从一个持久存储中,它的经理,部门,和报告关系是开始的被断层呈现。表13-1展示一个被断层呈现的雇员的部门关系。







Figure 13-1A department represented by a fault

 







Although the fault is an instance of the Department class, it has not yet been realized—none of its persistent instance variables have yet been set. This means that not only does the department object consume less memory itself, but there’s no need to populate its employees relationship. If it were a requirement that the object graph be complete, then to edit a single attribute of a single employee, it would ultimately be necessary to create objects to represent the whole corporate structure.

尽管这个断层是部门类的实例,它还没有被实际化-它的持久实例变量还没有被设置。这意外这不仅这个部门对象消耗更少的内存,也不必去产生它的雇员的关系。如果有一个需求这个图表对象被完成,然后去编辑一个单独雇员的单独属性,去创建对象去展现整个公司结构是最终必要的。


Firing Faults

触发断层

Fault handling is transparent—you do not have to execute a fetch to realize a fault. If at some stage a persistent property of a fault object is accessed, then Core Data automatically retrieves the data for the object and initializes the object . This process is commonly referred to as firing the fault. If you send the Department object a message to get, say, its name, then the fault fires—and in this situation Core Data executes a fetch for you to retrieve all the object's attributes. (See NSManagedObject for a list of methods that do not cause faults to fire.)

断层处理是显示的-你不必去执行一个获取去实例化一个断层。如果在一个阶段一个断层对象的持久化属性被访问,然后CD自动获取自动获取数据为那个对象和初始化那个对象。这个过程通常称作触发断层。如果你发送部门对象一个信息去获取,说,它的名字,然后断层触发-和在这个情景中CD为你处理获取去取得所有对象的属性。(参见NSManagedObject为一个不造成断层去触发的方法的列表。)

Core Data automatically fires faults when a persistent property (such as firstName) of a fault is accessed. However, firing faults individually can be inefficient, and there are better strategies for getting data from the persistent store (see Decreasing Fault Overhead). To deal efficiently deal with faults and relationships, see Fetching Managed Objects and Preventing a Fault from Firing.

CD自动触发断层当一个断层的持久存储属性(比如fristName)被访问。然而,单独触发断层是徒劳的,从更好的策略去从持久存储获取数据(参见Decreasing Fault Overhead).去高效的处理断层和关系,参见Fetching Managed Objects 和Preventing a Fault from Firing.


When a fault is fired, Core Data does not go back to the store if the data is available in its cache. With a cache hit, converting a fault into a realized managed object is very fast—it is basically the same as normal instantiation of a managed object. If the data is not available in the cache, Core Data automatically executes a fetch for the fault object; this results in a round trip to the persistent store to fetch the data, and again the data is cached in memory.

当一个断层被触发,CD不会回到存储中如果数据在它的高速缓存中是可用的。当一个高速缓存碰上,转化一个断层到一个实际化的管理对象是非常快的-它在基本上相同的和一个管理对象的正常实例化。如果数据在高速缓存是不可用的,CD自动处理一个获取为一个断层对象;这个结果在一个往返过程到持久存储去获取数据,和数据被再次缓存到内存中。


Whether or not an object is a fault simply means whether or not a given managed object has all its persistent attributes populated and is ready to use. If you need to determine whether an object is a fault, send it an isFault message without firing the fault (without accessing any relationships or attributes). If isFault returns NO, then the data must be in memory and therefore the object is not a fault. However, if isFault returns YES, it does not imply that the data is not in memory. The data may be in memory, or it may 

not, depending on many factors influencing caching.

一个对象是否是一个断层简单的意味着是否一个管理对象的所有属性全都产生出来和准备去使用。如果你需要去决定一个对象是否是断层,发送一个isFault信息而不用触发断层(不用访问任何关系和属性)。如果isFault返回NO,然后这个数据必须在内存中 因此这个对象不是一个断层。然而如果isFault返回YES,它没有在内存中实现哪些数据。这个数据可能在内存中,或它可能不在,取决于多少因素影响缓存。


Although the standard description method does not cause a fault to fire, if you implement a custom description method that accesses the object’s persistent properties, the fault will fire. You are strongly discouraged from overriding description in this way.

尽管标准description方法不造成一个断层去触发,如果你实现一个自定义访问对象持久属性的description方法,断层将触发。强烈建议不要重写description以这个方式。






There is no way to load individual attributes of a managed object on an as-needed basis and avoid realizing (retrieving all property values of) the entire object. For patterns to deal with large attributes, see Binary Large Data Objects (BLOBs).

没油方法去加载一个管理对象的单独属性作为一个必要的基础而不用实例化(获取所有属性的值)整个对象,处理大属性的模式,参见Binary Large Data Objects(BLOBs).


Turning Objects into Faults

转化对象到断层

Turning a realized object into a fault can be useful in pruning the object graph, as well as ensuring that property values are current. Turning a managed object into a fault releases unnecessary memory, and sets its in-memory property values to nil. (see Reducing Memory Overhead andEnsuring Data Is Up to Date )

转化一个实际对象到一个断层在修剪对象图表很有用,也保证属性值是当前的。转化一个管理对象到断层释放不必要的内存,和设置它的内存中的属性值到nil.(参见Reducing Memory Overhead 和保证数据是最新的)


You can turn a realized object into a fault with the refreshObject:mergeChanges: method. If you pass NO as the mergeChanges argument, you must be sure that there are no changes to that object’s relationships. If there are, and you then save the context, you will introduce referential integrity problems to the persistent store.

你能使用refreshObject:mergeChanges:方法转化一个实例对象到一个断层。如果你传递NO作为mergeChanges参数,你必须保证那个对象的关系没有发生改变。如果有改变,你然后保存了上下文,你将给持久存储产生引用完整性问题。

When an object turns into a fault, it is sent a didTurnIntoFault message. You may implement a custom didTurnIntoFault method to perform various housekeeping functions; for example, see Ensuring Data Is Up to Date  ).

当一个对象转化到一个断层,它会发送一个didTurnIntoFault信息。你可能实现一个自定义didTurnIntoFault方法去执行各种各样的功能;比如查看数据是否是最新的。







NOTE

Core Data avoids the term unfaulting because it is confusing. There’s no “unfaulting” a virtual memory page fault. Page faults are triggered, caused, fired, or encountered. Of course, you can release memory back to the kernel in a variety of ways (using the functions vm_deallocate, munmap, or sbrk). Core Data describes this as “turning an object into a fault”.


CD避免项目的去断层因为它是困惑的。并没有“非断层”一个虚拟内存页断层。页断层被触发造成发射或遇到。当你页能释放内存回到内核中在很多方法中(使用functions vm_deallocate, munmap,或 sbrk).CD把着称为”转化一个对象到断层”。


Faults and KVO Notifications

断层和KVO通知

When Core Data turns an object into a fault, key-value observing (KVO) change notifications are sent to the object’s properties. If you are observing properties of an object that is turned into a fault and the fault is subsequently realized, you receive change notifications for properties whose values have not in fact changed. See Key-Value Observing Programming Guide.

当CD转化一个对象到一个断层,键值观察(KVO)改变通知被发送到对象的属性。如果你观测一个被转化到断层对象的属性和这个断层随后实际化,你接受改变通知为值没有真正的改变的属性。


Although the values are not changing semantically from your perspective, the literal bytes in memory are changing as the object is materialized. The key-value observing mechanism requires Core Data to issue the notification whenever the values change according to the perspective of pointer comparison. KVO needs these notifications to track changes across key paths and dependent objects.

尽管值没有语义上的改变从你的观点,在内存中的字节数随着对象被物化而改变。这个键值检测机制需要CD去发布通知无论什么时候按照指针对比的观点值发生改变。KVO需要哪些通知去追中哪些改变通过key paths 和依赖对象。














Uniquing Ensures a Single Managed Object per Record per Context

独特保证一个单独的管理对象每个记录每个上下文


Core Data ensures that—in a given managed object context—an entry in a persistent store is associated with only one managed object. The technique is known as uniquing. Without uniquing, you might end up with a context maintaining more than one object to represent a given record.

For example, consider the situation illustrated in Figure 13-2; two employees have been fetched into a single managed object context. Each has a relationship to a department, but the department is currently represented by a fault.

CD保证-在一个给定的管理对象上下文中-一个在持久存储中实体和仅一个关系对象相关。这个计算被称作 独特,没有独特,你可能最终用一个上下文保持多个对象去呈现给定的记录。比如考虑在表13-2中阐述场景;两个雇员已经被获取到一个单独的管理对象上下文。每个有一个关系到一个部分,但是部门目前被一个断层呈现。







Figure 13-2Independent faults for a department object

 

It would appear that each employee has a separate department, and if you called department on each employee — turning the Department faults into regular objects — you would have two separate Department objects in memory. However, if both employees belong to the same department (for example, Marketing), then Core Data ensures that (in a given managed object context) only one object representing the Marketing department is ever created. If both employees belong to the same department, their department relationships would both therefore reference the same fault, as illustrated in Figure 13-3.

它显示每个雇员有一个分开的部门,和如果你调用department在每个雇员-转化这个部门断层到一个普通对象-你将有两个分开部门对象在内存中。然而,如果两个雇员属于相同的部门(比如,市场部),然后CD确保(在一个给定管理对象上下文)只有一个呈现市场部门的对象被创建一次。如果两个雇员属于相同的部门,他们部门关系将都引用到相同的断层,像表13-3阐明的那样。












Figure 13-3Uniqued fault for two employees working in the same department

 

Without uniquing, if you fetched all the employees and called department on each — thereby firing the corresponding faults — a new Department object would be created every time. This would result in a number of objects, each representing the same department, that could contain different and conflicting data. When the context is saved, it would be impossible to determine the correct data to commit to the store.

没有独特,如果你获取所有的雇员和每一个调用department-因此触发相应的断层-一个新部门对象会被创建每次。这将产生很多数量的对象,每个都展示相同的部门,哪些可能包含不同和冲突数据。当上下文被保持,去决定提交到存储正确的数据是不可能的。


More generally, all the managed objects in a given context that refer to the Marketing Department object refer to the same instance—they have a single view of Marketing’s data—even if the Marketing Department object is a fault.

更常见的,所有在一个给定的上下文中的管理对象引用到市场部门对象指向相同的实例-他们有一个单独的市场数据的表现-就算市场部门对象是一个断层。

NOTE

This discussion focuses on a single managed object context. Each managed object context represents a different view of the data. If the same employees are fetched into a second context, then they—and the corresponding Department object—are all represented by different objects in memory. The objects in different contexts may have different and conflicting data. It is precisely the role of the Core Data architecture to detect and resolve these conflicts at save time.

这个讨论专注在一个单独的管理对象上下文。每个管理对象上下文展示一个数据的不同视图。如果相同雇员被获取到第二个上下文,然后她们-和对应部门对象-都被在内存中的不同对象创建。在不同上下文中的对象有不同和冲突数据,这正是CD结构的角色去检测和解决哪些冲突在保存时间。








Object Validation

对象验证

Cocoa provides a basic infrastructure for model value validation. However, it requires you to write code for all the constraints you want to apply. Core Data, on the other hand, allows you to put validation logic into the managed object model and specify most common constraints as opposed to writing validation logic in your code. You can specify maximum and minimum values for numeric and date attributes, maximum and minimum lengths for string attributes, and a regular expression that a string attribute must match. You can also specify constraints on relationships, such as making them mandatory or unable exceed a certain number.

Cocoa提供了一个基础建设为模型值验证。然而,它需要你去写代码为所有你想要去实现的限制。CD,在另一个方面,允许i去放置验证逻辑到管理对象模型和明确很多常见限制作为与写入验证逻辑相反的在你的代码中。你能明确最大值和最小值为数字和日期属性,最大和最小长度为字符串属性,和一个字符串属性必须匹配的表达。你也能在关系上明确限制,比如让他们强制或不可用超过一个固定数字。







If you do want to customize validation of individual properties, you use standard validation methods as defined by the NSKeyValueCoding protocol and described in Implementing Custom Property-Level Validation. To validate combinations of values (such as an array) and relationships, see Implementing Custom Interproperty Validation.

如果你想去自定义单独属性的验证,你使用标准验证方法被NSKeyValueCoding协议定义和描述在Implementing Custom Property-Level Validation.去验证值的组合(比如一个数组)和关系,参见Implementing Custom Interproperty  Validation.


How Validation Works in Core Data

  如何验证在CD中的工作

How to validate is a model decision, when to validate is a user interface or controller-level decision. For example, a value binding for a text field might have its “validates immediately” option enabled. Moreover, at various times, inconsistencies are expected to arise in managed objects and object graphs.

An in-memory object can temporarily become inconsistent. 

如何去验证是模型的决定,什么时候去验证是用户界面或控制-层级决定。比如一个值绑定到一个文本区域可能有它的“立即验证”选项可用。更多在很多时间,不一致被期待去升高在管理对象和对象图表中。一个 在-内存对象能暂时不一致。


The validation constraints are applied by Core Data only during a save operation or upon request (you can invoke the validation methods directly at any time it makes sense for your application flow). Sometimes it is useful to validate changes as soon as they are made and to report errors immediately. Other times it makes sense to wait until a unit of work is completed before validation takes place. 

这验证限制只有在一个保存操作或基于请求被CD应用(你能直接唤醒验证方法在在它让你的应用流向有意义的任何时间)。有时去验证改变在她们被创造后很有用和立即报告错误。其他时间去等待知道一个工作的单元被完成在验证发生前有意义。


If managed objects were required to be always in a valid state, it would amongst other things force a particular workflow on the user. The ability to work with managed objects when they are not in a valid state also underpins the idea of a managed object context representing a scratch pad — in general you can bring managed objects onto the scratch pad and edit them however you wish before ultimately committing the changes or discarding them.

如果管理对象被要求去常常在一个确认状态,它将在其他事情强制一个特色工作流程在用户上。去使用管理对象当他们不在验证状态也支撑一个管理对象上下文展示一个便签簿的注意-通常来说你能把管理对象带到便签簿上和编辑他们然而你希望在你最终提交改变或废弃它们之前。



Implementing Custom Property-Level Validation

实现自定义属性-层级验证

The NSKeyValueCoding protocol specifies the validateValue:forKey:error: method to provide general support for validation methods in a similar way to that in which valueForKey: provides support for accessor methods.

这个NSKeyValueCoding协议明确validatevalue:forKey:error方法去提供通用支撑为验证方法在一个相似方法 在那个方法valueForKey:提供支撑为访问方法。


NSManagedObject provides consistent hooks for implementing property (and interproperty) values. If you want to implement logic in addition to the constraints you provide in the managed object model, do not override validateValue:forKey:error:. Instead implement methods of the form validate<Key>:error:. If you do implement custom validation methods, you should typically not invoke them directly. Instead call the general method validateValue:forKey:error: with the appropriate key. This ensures that any constraints defined in the managed object model are also applied. 

If you were to call validate<Key>: error: instead, constraints may not be applied.

NSManagedObject提供一致性钩为属性(和内部属性)实现。如果你想去实现逻辑在添加到你在管理对象模型提供的限制上,不要从写validateValue:forKey:error:.而是实现validate<Key>:error:形式的方法实现。如果你实现自定义验证方法,你不应该直接使用它们。而是使用合适的key调用通用方法validateValue:forKey:error:.这个保证任何在管理对象模型中定义的限制也被应用。如果你去调用validate<Key>:error:限制可能不被应用。




In the method implementation, you check the proposed new value, and if it does not fit your constraints, you return NO. If the error parameter is not null, you also create an NSError object that describes the problem, as illustrated in the following example. The example validates that the age value is greater than zero. If it is not, an error is returned.

在方法实现,你检查提出的新值,和如果它不符合你的限制,你返回NO。如果错误参数不是null,你也创建一个描述问题的NSError对象,就像下面例子中阐明的那样。这个例子验证年龄值比零大,如果它不是,一个错误被返回。

OBJECTIVE-C

- (BOOL)validateAge:(id*)ioValue error:(NSError**)outError

{

    if (*ioValue == nil) {

        return YES;

    }

    if ([*ioValue floatValue] <= 0.0) {

        if (outError == NULL) {

            return NO;

        }

        NSString *errorStr = NSLocalizedStringFromTable(@"Age must be greater than zero", @"Employee", @"validation: zero age error");

        NSDictionary *userInfoDict = @{NSLocalizedDescriptionKey: errorStr};

        NSError *error = [[NSError alloc] initWithDomain:EMPLOYEE_ERROR_DOMAIN code:PERSON_INVALID_AGE_CODE userInfo:userInfoDict];

        *outError = error;

        return NO;

    } else {

        return YES;

    }

}

SWIFT

func validateAge(value a href="" AutoreleasingUnsafeMutablePointer /a < a href="" AnyObject /a ?>) throws {

    if value == nil {

        return

    }

   

    let valueNumber = value.memory as a href="" NSNumber /a 

    if valueNumber.floatValue > 0.0 {

        return

    }

    let errorStr = NSLocalizedString("Age must be greater than zero", tableName: "Employee", comment: "validation: zero age error")

    let userInfoDict = [NSLocalizedDescriptionKey: errorStr]

    let error = NSError(domain: "EMPLOYEE_ERROR_DOMAIN", code: 1123, userInfo: userInfoDict)

    throw error

}

The input value is a pointer to an object reference (an id *). This means that in principle you can change the input value. However, doing so is strongly discouraged, as there are potentially serious issues with memory management (see Key-Value Validation in Key-Value Coding Programming Guide). Moreover, do not call validateValue:forKey:error: within custom property validation methods. If you do, you will create an infinite loop when validateValue:forKey:error: is invoked at runtime.

这个输入值是一个指向一个对象的指针(一个id *).这意外着原则上你能改变输入的值。然而强烈不建议这样做,因为在内存管理(参见在 Key-Value Coding 编程指导中的  Key-Value Validation )上有潜在的严重问题。还有不要在自定义属性验证方法中调用validateValue:forKey:errorr:如果你这样做你会创建一个无限循环当validateValue:forKey:error被在运行时唤醒时。


Do not change the input value in a validate<Key>:error: method unless the value is invalid or uncoerced. The reason is that, because the object and context are now dirtied, Core Data may validate that key again later. If you keep performing a coercion in a validation method, it can produce an infinite loop. Similarly, also be careful if you implement validation and willSave methods that produce mutations or side effects—Core Data will revalidate those changes until a stable state is reached.

不要改变输入的值在一个validate<Key>:error:方法中除非值是不可用的或不正确的或非强迫的。着原因是,因为对象和上下文现在是污染的,CD可能在以后再一次验证那个key. 如果你保证执行一个强制在一个验证方法中,它可能创建一个无限循环。相似的,也要小心如果你实现验证和willSave方法产生突变和副作用-CD将再次验证哪些改变直到达到一个稳定状态。













Implementing Custom Interproperty Validation

实现自定义内部属性验证

It is possible for the values of all the individual attributes of an object to be valid and yet for the combination of values to be invalid. Consider, for example, an application that stores people’s age and whether or not they have a driving license. For a Person object, 12 might be a valid value for an age attribute, and YES is a valid value for a hasDrivingLicense attribute, but (in most countries at least) this combination of values would be invalid.

NSManagedObject provides additional opportunities for validation — update, insertion, and deletion—through the validateFor… methods such as validateForUpdate:. If you implement custom interproperty validation methods, you call the superclass’s implementation first to ensure that individual property validation methods are also invoked. If the superclass' implementation fails (that is, if there is an invalid attribute value), then you can do one of the following:

一个对象的所有独立属性被验证是有效的可是值的组合是无效的。考虑一下,比如,一个应用存储人的年龄和它们是否有一个驾照。对一个人对象,12可能是一个有效值对一个age属性,和YES是一个有效值对一个hasDrivingLicense 属性,但是(在很多国家至少)这个值的组合将是无效。NSManagedObject提供额外的机会为验证-更新,插入,和删除-尽管这个validateFor...方法比如validateForUpdate:.如果你实现自定义的内部验证方法,你首先调用父类的实现去保证单独属性验证方法也是唤醒的。如果父类实现失败(也就是,如果有一个不可用的属性值),然后你能做如下的事:


Return NO and the error created by the superclass' implementation. 

返回NO 和被父类实现创建的错误。

Continue to perform validation, looking for inconsistent combinations of values. 

继续去执行验证,查找值的不一致组合。

If you continue to perform validation, make sure that any values you use in your logic are not themselves invalid in such a way that your code might itself cause errors. For example, suppose you use an attribute whose value is 0 as a divisor in a computation, but the attribute is required to have a value greater than 0. Moreover, if you discover further validation errors, you must combine them with the existing error and return a “multiple errors error” as described in Combining Validation Errors.

如果你去执行验证,保证在你的逻辑中你使用的任何值不是它们自身不可用在你的代码可能自己产生错误的方法中。比如假设你使用一个属性它的值是0作为一个除数在计算中,但是这个属性被要求有一个大于0的值。更多如果你发现更深验证错误,你必须组合它们和存在错误 和返回一个在Combining Validation Errors 中描述的组合了错误的错误。


The following example shows the implementation of an interproperty validation method for a Person entity that has two attributes, birthday and hasDrivingLicense. The constraint is that a person younger than 16 years cannot have a driving license. This constraint is checked in both validateForInsert: and validateForUpdate:, so the validation logic itself is factored into a separate method.

下面例子展示一个为一个有两个个属性,birthday 和 hasDrivingLicense 的人 实体内部属性验证方法的实现.限制是一个小于16年的人不能有驾照。这个限制被在validateForInsert:和validateForUpdate:中检测,所以这个验证逻辑它自己是分解到一个分开方法中国。


Listing 14-1Interproperty validation for a Person entity

OBJECTIVE-C

- (BOOL)validateForInsert:(NSError **)error

{

    BOOL propertiesValid = [super validateForInsert:error];

    // could stop here if invalid

    BOOL consistencyValid = [self validateConsistency:error];

    return (propertiesValid && consistencyValid);

}

- (BOOL)validateForUpdate:(NSError **)error

{

    BOOL propertiesValid = [super validateForUpdate:error];

    // could stop here if invalid

    BOOL consistencyValid = [self validateConsistency:error];

    return (propertiesValid && consistencyValid);

}

- (BOOL)validateConsistency:(NSError **)error

{

    static NSCalendar *gregorianCalendar;

    NSDate *myBirthday = [self birthday];

    if (myBirthday == nil) {

        return YES;

    }

    if ([[self hasDrivingLicense] boolValue] == NO) {

        return YES;

    }

    if (gregorianCalendar == nil) {

        gregorianCalendar = [[NSCalendar alloc] initWithCalendarIdentifier:NSCalendarIdentifierGregorian];

    }

    NSDateComponents *components = [gregorianCalendar components:NSCalendarUnitYear fromDate:myBirthday toDate:[NSDate date] options:0];

    NSInteger years = [components year];

    if (years >= 16) {

        return YES;

    }

    if (error == NULL) {

        //don't create an error if none was requested

        return NO;

    }

    NSBundle *myBundle = [NSBundle bundleForClass:[self class]];

    NSString *drivingAgeErrorString = [myBundle localizedStringForKey:@"TooYoungToDriveError" value:@"Person is too young to have a driving license." table:@"PersonErrorStrings"];

    NSMutableDictionary *userInfo = [NSMutableDictionary dictionary];

    [userInfo setObject:drivingAgeErrorString forKey:NSLocalizedFailureReasonErrorKey];

    [userInfo setObject:self forKey:NSValidationObjectErrorKey];

    NSError *drivingAgeError = [NSError errorWithDomain:EMPLOYEE_ERROR_DOMAIN code:NSManagedObjectValidationError userInfo:userInfo];

    if (*error == nil) { // if there was no previous error, return the new error

        *error = drivingAgeError;

    } else { // if there was a previous error, combine it with the existing one

        *error = [self errorFromOriginalError:*error error:drivingAgeError];

    }

    return NO;

}

SWIFT

override func validateForInsert() throws {

    try super.validateForInsert()

    try self.validateConsistency()

}

override func validateForUpdate() throws {

    try super.validateForUpdate()

    try self.validateConsistency()

}

func validateConsistency() throws {

    guard let myBirthday = birthday else {

        return

    }

    if !hasDrivingLicense {

        return

    }

   

    let gregorianCalendar = NSCalendar(calendarIdentifier: NSCalendarIdentifierGregorian)!

   

    let components = gregorianCalendar.components(.Year, fromDate: myBirthday, toDate: NSDate(), options:.WrapComponents)

    if components.year >= 16 {

        return

    }

   

    let errString = "Person is too young to have a driving license."

    let userInfo = [NSLocalizedFailureReasonErrorKey: errString, NSValidationObjectErrorKey: self]

    let error = NSError(domain: "EMPLOYEE_ERROR_DOMAIN", code: 1123, userInfo: userInfo)

    throw error

}


Combining Validation Errors

组合验证错误

If there are multiple validation failures in a single operation, you create and return an NSError object with the code NSValidationMultipleErrorsError (for multiple errors error). You add individual errors to an array and add the array—using the key NSDetailedErrorsKey — to the user info dictionary in the NSError object. This pattern also applies to errors returned by the superclass's validation method. Depending on how many tests you perform, it may be convenient to define a method that combines an existing NSError object (which may itself be a multiple errors error) with a new one and returns a new multiple errors error.

如果在有很多验证失败在一个单独操作,你创建和使用代码NSValidationMultipleErrorsError(为组合错误的错误)返回NSError对象。你添加单个错误到一个数组和添加这个数组-使用NSDetailedErrorskey这个键-到NSError 对象的用户信息字典。这个模式也应到通过父类验证方法返回的错误。取决于你执行多少操作,去定义一个组合一个存在NSError 对象(也可能它自己就是一个组合错误的错误)和一个新的 然后返回一个新的组合错误方法可能是便利的。

The following example shows the implementation of a simple method to combine two errors into a single multiple errors error. How the combination is made depends on whether or not the original error was itself a multiple errors error. If the original error was already a multiple errors error, then the second error is added to it. Otherwise the two errors are combined together to create a new multiple errors error.

下面例子展示一个简单去组合两个错误到一个单独组合了错误的错误方法的实现。怎么组合是基于是否原始的错误是否是一个组合错误。如果原始错误已经是一个组合了错误的错误,然后第二个错误被添加上。否则两错误组合到一起去创建一个新的组合了错误的错误。


NOTE

The combining of errors is not currently available in Swift.

组合错误在Swift中当前是不可用的。


Listing 14-2A method for combining two errors into a single multiple errors error

- (NSError *)errorFromOriginalError:(NSError *)originalError error:(NSError*)secondError

{

    NSMutableDictionary *userInfo = [NSMutableDictionary dictionary];

    NSMutableArray *errors = [NSMutableArray arrayWithObject:secondError];

    if ([originalError code] == NSValidationMultipleErrorsError) {

        [userInfo addEntriesFromDictionary:[originalError userInfo]];

        [errors addObjectsFromArray:[userInfo objectForKey:NSDetailedErrorsKey]];

    } else {

        [errors addObject:originalError];

    }

    [userInfo setObject:errors forKey:NSDetailedErrorsKey];

    return [NSError errorWithDomain:NSCocoaErrorDomain code:NSValidationMultipleErrorsError userInfo:userInfo];

}











Change Management

改变管理

If your application contains more than one managed object context and you allow objects to be modified in more than one context, you need to be able to reconcile the changes. This is a fairly common situation when an application is importing data from a network and the user can also edit that data.

如果你的应用包含超过一个管理对象上下文和你允许对象在超过一个上下文中被改变,你需要去能调和哪些改变。这是一个相当普遍的情况当一个应用从网络输入数据和用户能编辑哪些数据。


Multiple Contexts in One Application

多个上下文仔一个应用

The object graph associated with any given managed object context must be internally consistent. If you have multiple managed object contexts in the same application, however, it is possible that each may contain objects that represent the same records in the persistent store, but whose characteristics are mutually inconsistent. In an employee application, for example, you might have two separate windows that display the same set of employees, but distributed between different departments and with different managers, as shown in Figure 15-1. Note how the manager relationship has moved from Lau to Weiss. Managed object context 1 still represents what is on disk, but managed object context 2 has changed.

这个对象图表相关使用的任何给定的管理对象上下文必须是内部一致性的。如果你有很多管理对象上下文在相同应用,然而,每个可能包含呈现相同的在持久存储中的记录的对象,但是它的特点是相互不一致的。在一个雇员应用,比如,你可能有两个分开的窗口展示相同集合的雇员,但是分布在不同部门和不同经理之间,就像表15-1展示的。注意经理关系已经从Lau移到Weiss.管理对象上下文1依旧呈现磁盘上的内容,但是管理对象上下文2已经改变。



















Figure 15-1Managed object contexts with mutually inconsistent data values

 

Ultimately there can be only one truth, and differences between these views must be detected and reconciled when data is saved. When one of the managed object contexts is saved, its changes are pushed through the persistent store coordinator to the persistent store. When the second managed object context is saved, conflicts are detected using a mechanism called optimistic locking. How the conflicts are resolved depends on how you have configured the context.

最终只有一个能是真值,和哪些视图之间的不同必须被检测和一致当数据保存的时候。当一个管理对象上下文被保存,它的改变被通过持久存储协调器到推到持久存储。当第二个管理对象上下文被保存,冲突被检测到 使用一个叫开放式锁定的机制。冲突被如何解决取决于你已经配置的上下文是什么样的。








Conflict Detection and Optimistic Locking

冲突检测和开放锁

When Core Data fetches an object from a persistent store, it takes a snapshot of its state. A snapshot is a dictionary of an object’s persistent properties—typically all its attributes and the global IDs of any objects to which it has a to-one relationship. Snapshots participate in optimistic locking. When the framework saves, it compares the values in each edited object’s snapshot with the then-current corresponding values in the persistent store.

***当CD获取一个对象从一个持久存储,它采取一个它(对象)的状态的快照。一个快照是一个对象的持久属性-通常它的所有属性和任何对象的有一个对一关系的全局IDs  的一个字典。快照参与开发锁定。当这个框架保存,它对比在每一个被编辑对象的快照和 以后-当前 相应在持久存储中的值。

If the values are the same, then the store has not been changed since the object was fetched, so the save proceeds normally. As part of the save operation, the snapshots' values are updated to match the saved data. 

如果值是相同的,然后存储还没有被改变自对象被获取以来,所以保存处理正常。作为保存操作的一部分,这个快照值被更新去匹配被保存的数据。


If the values differ, then the store has been changed since the object was fetched or last saved; this represents an optimistic locking failure. The conflict must be resolved.

如果值不同,那么存储已经被改变从对像被获取或上次保存以后;这个展示开发锁失败冲突必须被解决****。


Choosing a Merge Policy

选择合并策略

You can get an optimistic locking failure if more than one Core Data stack references the same external data store regardless of whether you have multiple Core Data stacks in a single application or you have multiple applications. It is possible that the same conceptual managed object will be edited in two persistence stacks simultaneously. You may want to ensure that subsequent changes made by the second stack do not overwrite changes made by the first, but other behaviors may be appropriate. Choose a merge policy for the managed object context that is suitable for your situation.

你能获取到一个开发锁失败如果超过一个CD栈引用到相同的外部数据存储忽略你是否有多个CD栈在一个单独应用或你有多个应用。相同概念管理对象将在两个持久栈中同时被编辑是可能的。你可能想去保证随后被第二个栈产出的改变不覆盖第一个栈的改变,但是其他的行为都是相似的。选择一个合并方案为管理对象上下文对你的场景是合适的。


The default behavior is defined by an NSErrorMergePolicy property. This policy causes a save to fail if there are any merge conflicts. The save method returns an error with a userInfo dictionary that contains the key @"conflictList"; the corresponding value is an array of conflict records. You use the array to tell the user the differences between the values the user is trying to save and those current in the store. In this scenario the user must fix the conflicts (by refetching objects so that the snapshots are updated).

默认行为通过NSErrorMergePolicy属性定义。这个方案造成一个保存的失败如果有很多合并冲突。这保存方法返回一个带有包含了@“conflictList”键的userInfo字典错误;对应的值是一个冲突记录的数组。你使用这个数组去告诉用户他尝试去保存的值和当前在存储中的值之间的不同。在这个场景中用户必须修复这个冲突(通过过去对象让快照被更新)。


Alternatively, you specify a different policy. The NSErrorMergePolicy is the only policy that generates an error. Other policies — NSMergeByPropertyStoreTrumpMergePolicy, NSMergeByPropertyObjectTrumpMergePolicy, and NSOverwriteMergePolicy — allow the save to proceed by merging the state of the edited objects with the state of the objects in the store in different ways. The NSRollbackMergePolicy discards in-memory state changes for objects in conflict and uses the persistent store’s version of the objects’ state.

或者,你明确一个不同策略。这个NSErrorMergePolicy是唯一的一个产生错误的策略。其他策略-NSMergeByPropertyStoreTrumpMergePolicy,

NSMergeByPropertyObjectTrumpMergePolicy,

和NSOverwriteMergePolicy-允许通过混合编辑对象的状态和在存储中对像的状态的保存去执行。这个NSRollbackMergePolicy丢弃对象在内存状态改变在冲突中的和使用持久存储的对象状态的版本。















Automatic Snapshot Management

自动快照管理

An application that fetches hundreds of rows of data can build up a large cache of snapshots. Theoretically, if enough fetches are performed, a Core Data-based application can contain all the contents of a store in memory. Clearly, snapshots must be managed in order to prevent this situation.

Responsibility for cleaning up snapshots rests with a mechanism called snapshot reference counting. This mechanism keeps track of the managed objects that are associated with a particular snapshot—that is, managed objects that contain data from a particular snapshot. When there are no remaining managed object instances associated with a particular snapshot (which Core Data determines by maintaining a list of strong references), Core Data automatically breaks the reference to the snapshot and it is removed from memory.

一个获取几百行数据的应用快照能产生大量的屏幕快照缓存。理论上,如果足够获取被执行,一个CD基础应用能包含一存储的所有内容在内存中。明显,快照必须被管理为了防止这个场景。负责使用一个机制清除剩余的快照被叫做快照引用计算。这个机制保持联系到一个特色快照的管理对象的追踪-也就是,包含了从一个特殊快照获取水管理对象。当没有从那个特殊快照(CD通过强引用的保持列表决定的)相关的保留的管理对象,CD自动打破到那个快照的引用和它被从内存中移除。


Synchronizing Changes Between Contexts

同步上下文之间的改变

If you use more than one managed object context in an application, Core Data does not automatically notify one context of changes made to objects in another. In general, this is because a context is intended to be a scratch pad where you can make changes to objects in isolation, and you can discard the changes without affecting other contexts. If you do need to synchronize changes between contexts, how a change should be handled depends on the user-visible semantics you want in the second context, and on the state of the objects in the second context.

如果你使用超过一个管理对象上下文在一个应用中,CD不会自动的通知一个上下文中一个对象的改变到另一个上下文。通常来说这个应用一个上下文趋向于是一个你能单独的对对象做改变的一个便签本,和你能放弃哪些改变而不用影像其他上下文。如果你需要去同步上下文间的改变,一个改变应该如何被处理基于你想在第二个上下文中想让用户-可见的场景,和在第二个上下文中对象的状态。


Registering with NSNotificationCenter

使用通知中心注册

Consider an application with two managed object contexts and a single persistent store coordinator. If a user deletes an object in the first context (moc1), you may need to inform the second context (moc2) that an object has been deleted. In all cases, moc1 automatically posts an NSManagedObjectContextDidSaveNotification notification via the NSNotificationCenter that your application should register for and use as the trigger for whatever actions it needs to take. This notification contains information not only about deleted objects, but also about changed objects. You need to handle these changes because they may be the result of the delete. Most of these types of changes involve transient relationships or fetched properties.

考虑一个应用使用两个管理对象上下文和一个单独持久存储协调器。如果一个用户删除第一个上下文(moc1),你可能需要去通知第二个上下文(moc2)一个对象已经被删除。在所有情况中,moc1自动发送一NSManagedObjectContextDidSaveNotification 通知通过通知中心你的应用应该注册和作为任何它需要去采取动作的触发。这个通知不仅包含删除对象的信息,也管用改变对象。你需要去处理哪些改变因为它们可能是删除的结果。很多那种类型的改变包含展示属性或获取属性。


Choosing a Synchronization Strategy

选择一个同步策略

When deciding how you want to handle your delete notification, consider:

What other changes exist in the second context? 

Does the instance of the object that was deleted have changes in the second context? 

Can the changes made in the second context be undone? 

当决定你想如何去处理你的删除通知,考虑:
什么改变存在于第二个上下文中?

在第二个上下文中被删除对象的实例已经改变了没。

这个在第二个上下文中的改变是否能被撤销


These concerns are somewhat orthogonal, and what actions you take to synchronize the contexts depend on the semantics of your application. The following three strategies are presented in order of increasing complexity.

哪些考虑事有有些正交的你采取去同步上下文的动作取决于你的应用的场景。一下的三个策略被呈现为了增加复杂性。



1 The object itself has been deleted in moc1 but has not changed in moc2. In that situation you do not have to worry about undo, and you can just delete the object in moc2. The next time moc2 saves, the framework will notice that you are trying to redelete an object, ignore the optimistic locking warning, and continue without error. 

在moc1中的对象自己已经被删除但是在moc2还没有改变。在这个场景你不必去担心撤销,你只需删除在moc2中的对象.下次moc2保存,框架将主演你正在再次删除一个对象,忽略开放式锁定警告,继续而没有错误。

2 If you do not care about the contents of moc2, you can simply reset it (using reset) and refetch any data you need after the reset. This will reset the undo stack as well, and the deleted object is now gone. The only issue here is determining what data to refetch. Do this before you reset by collecting the IDs (objectID) of the managed objects you still need and using those to reload once the reset has happened. You must exclude the deleted IDs, and it is best to create fetch requests with IN predicates to ensure faults will be fulfilled for deleted IDs. 

如果你不在乎moc2中的内容,你能简单的复位它(使用 reset)和获取任何你需要的数据在复位以后。这将也会复位撤销栈,被删除的对象现在不见了。这里的问题是决定去获取什么数据。在做这个之前你通过收集你依旧需要管理对象的IDs(objectID)复位和使用哪些去再次加载一次 复位就发生了。你必须排除删除的IDs,最好是去创建获取请求使用IN谓词去保证删除IDs断层将被完成。

3 If the object has changed in moc2, but you do not care about undo, your strategy depends on what it means for the semantics of your application. If the object that was deleted in moc1 has changes in moc2, should it be deleted from moc2 as well? Or should it be resurrected and the changes saved? What happens if the original deletion triggered a cascade delete for objects that have not been faulted into moc2? What if the object was deleted as part of a cascade delete? 

如果在moc2的对象已经改变,但是你不在乎撤销,你的策略基于对你的应用的情景这意外着什么。如果在moc1中的被删除的对象已经在moc2,改变,它应该也被从moc2中删除吗?或者它应该被复活和保存改变?如果原始的删除触发了级连删除还没有断层到moc2中的对象会发生什么?要是这个对象被删除是作为一个级连删除的一部分?





There are two workable options: 

有两个可行的选择:

Simply discard the changes by deleting the object in the moc that is receiving the notification. 

通过删除在moc中的接受通知的对象简单的忽略改变。

Alternatively, if the object is standalone, set the merge policy on the context to NSOverwriteMergePolicy. This policy will cause the changes in the second context to overwrite the delete in the database. 

另外,如果这个对象是独立的,设置在上下文的合并策略为NSOverwriteMergePolicy.这个策略造成在第二个上下文的改变去覆盖那个在数据基础中的删除。
it will cause all changes in
moc2 to overwrite any changes made in moc1

它将造成所有在moc2的改变去覆盖所有在moc1中的发生的改变。


The preceding solutions are least likely to leave your object graph in an unsustainable state as a result of something you missed. If you find your application hitting merge issues that you are not able to resolve, this generally indicates an issue with the application’s architecture.

前面的解决最少可能性去 遗留你的对象图表在一个不可支持的状态 做为你错过一些事情的结果。如果你发现你的应用碰到你不能够解决的合并问题,这个通常标示着应用的架构出了问题。























Persistent Store Types and Behaviors

Core Data provides an in-memory persistent store and three disk-based persistent stores, as described in Table 16-1. The binary store (NSBinaryStoreType) is an atomic store, as is the XML store ( a href="" NSXMLStoreType /a ). You can also create custom store types, atomic and incremental. See Atomic Store Programming Topics and Incremental Store Programming Guide.


NOTE

The XML store is not available in iOS.


Table 16-1Built-in persistent store types

Store type

Speed

Object graph in memory

Other factors

XML (atomic)

Slow

Whole

Externally parsable

Binary (atomic)

Fast

Whole

N/A

SQLite

Fast

Partial

N/A

In-memory

Fast

Whole

No on-disk storage required


IMPORTANT

Although Core Data supports SQLite as a store type, the store format—like those of the other native Core Data stores—is private. You cannot create a SQLite database using the native SQLite API and use it directly with Core Data, nor should you manipulate an existing Core Data SQLite store using native SQLite API. If you have an existing SQLite database, you need to import it into a Core Data store.

尽管CD支持SQLite作为一个存储方式,这个存储形式-像其他的原生CD存储-是私有的。你不创建一个SQLite数据基础使用原生SQLite API 然后和CD一起使用它,你也不应该使用原生的SQLite API 操纵一个存在CD SQLite存储.如果你有一个存在的SQLite 数据基库,你不需要去倒入它作为一个CD存储。

Given the abstraction that Core Data offers, there is typically no need to use the same store throughout the development process. It is common, for example, to use the XML store early in a project life cycle, because it is fairly human-readable and you can inspect a file to determine whether or not it contains the data you expect. In a deployed application that uses a large data set, you typically use an SQLite store, because it offers high performance and does not require that the entire object graph reside in memory. You might use the binary store store if you want store writes to be atomic.

给一个CD提供的抽象,没有必要去使用相同的存储在开发过程期间。它很常见,比如,去用XML存储在项目生命循环早期,因为他是相当易读的和你能检查一个文件去决定是否它包含你期望的数据。在一个被开发的使用大数据集合应用中,你使用一个SQLite存储,因为它提供高速执行和不需要整个对象图表在内存中。你可能使用二进制存储 如果你想让存储写入是原子性的。


Limitations of Persistent Store Security

持久存储的局限性

Core Data makes no guarantees regarding the security of persistent stores from untrusted sources (as opposed to stores generated internally) and cannot detect whether files have been maliciously modified. The SQLite store offers slightly better security than the XML and binary stores, but it should not be considered inherently secure. Note also that it is possible for data archived in the metadata to be tampered with independently of the store data. To ensure data security, use a technology such as an encrypted disk image.

CD不保证关于从不信任的源(相对于 存储的内部产生的 )持久存储的安全和不能检测是否文件已经被恶意修改。这个SQLite存储提供比XML和二进制更安全轻便,但是它不能被认为是本身安全的。注意数据归档到数据元中也可能被存储数据的独立篡改。去保证数据安全,使用一个技术比如一个加密磁盘影像。


Fetch Predicates and Sort Descriptors

获取谓词和集合描述

Fetching differs somewhat according to the type of store. In the XML, binary, and in-memory stores, evaluation of the predicate and sort descriptors is performed in Objective-C with access to all Cocoa functionality, including the comparison methods on NSString.

获取不同有点依据于存储类型。在XML,二进制,和内存存储中,谓词和集合描述的赋值是在Objective-C中使用访问到所有Cocoa功能执行的,包括在NSString的对比方法。




The SQLite store, on the other hand, compiles the predicate and sort descriptors to SQL and evaluates the result in the database itself. This is done primarily for performance, but it means that evaluation happens in a non-Cocoa environment, and so sort descriptors (or predicates) that rely on Cocoa cannot work. The supported sort selectors for SQLite are compare: and caseInsensitiveCompare:, localizedCompare:, localizedCaseInsensitiveCompare:, and localizedStandardCompare:. The latter is Finder-like sorting, and what most people should use most of the time. In addition you cannot sort on transient properties using the SQLite store.

这个SQLite存储,在其他方面,编译谓词和集合描述到SQL 和 评估结果在自己数据基础中。这个首先为了执行,但是它意外着评估发生在一个非Cocoa环境中,和依赖在Cocoa集合描述(或谓词)不能工作.对SQLite支持的方法集合有compare: 和caseInsensitiveCompare:,localizedCompare:,localizedCaseInsensitiveCompare:,和localizedStandardCompare:,后者是搜索排序,和大多数人在更多时候应该的。另外你不能使用一个SQLite存储排序一个展示属性。


There are additional constraints on the predicates you can use with the SQLite store:

你使用SQLite 存储 这有其他在谓词上的限制

You cannot necessarily translate arbitrary SQL queries into predicates. 

You can have only one to-many element in a key path in a predicate.
For example, no
toOne.toMany.toMany, or toMany.toOne.toMany type constructions (they evaluate to sets of sets) are allowed. As a consequence, in any predicate sent to the SQL store, there may be only one operator (and one instance of that operator) from ALL, ANY, and IN

你不能把SQL查询转化到谓词。你只能有一个 一到多元素在一个谓词的一个钥匙路径。比如,不能toOne.toMany.toMany,或toMany.toOne.toMany类型结构(它们预测到集合的集合)是允许的。作为一个后果,在任何发送到SQL存储的谓词中,可能只有操纵(和那个操纵的一个实例)从ALL,ANY,和IN。







CoreData supports a noindex: that can be used to drop indices in queries 

passed to SQLite. This is done primarily for performance reasons: SQLite uses a limited number of indices per query, and noindex: allows the user to preferentially specify which indexes should not be used. See NSPredicate documentation regarding function expressions.

CD支持一个noindex:能被用来去减少传递到SQLite中的请求的指数。这个首先是为了执行的原因:SQLite使用数量有限的指数每个请求,和noinex:允许用户去偏好明确那个指标不应该被使用。 参见关于功能表达的NSPredicate文档。 


SQLite-Supported File Systems

SQLite-支持文件系统

The SQLite store supports reading data from a file that resides on any type of file system. However, the SQLite store does not generally support writing directly to file systems that do not implement byte-range locking. For DOS file systems and for some NFS file system implementations that do not support byte-range locking correctly, SQLite will use <dbfile>.lock locking, and for SMB file systems it uses flock-style (file level) locking.

SQLite存储支持读数据从一个在任何文件系统类型中的文件。然而这个SQLite存储不通用的支持直接写入到没有实现 字节-范围 锁定的文件系统.比如DOS文件系统和一些NFS文件系统实现不正确的支持 字节-范围 锁定,SQLite将使用<dbfile>.lock锁定,和SMB文件系统它使用 群-风格(文件级别)锁定。


To summarize: Byte-range locking file systems have the best concurrent read/write support; these include HFS+, AFP, and NFS. File systems with simple file locking are also supported, but do not allow for as much concurrent read/write access by multiple processes. Simple file locking systems include SMB and DOS. The SQLite store does not support writing to WebDAV file-systems.

总结来说:字节-范围 锁定文件系统有最好同步读/写支持;其中包括 HFS+

AFP,和NFS。使用简单文件锁定的文件系统也支持,但是不允许被多个线程同时读/写访问。简单文件锁定系统包括SMB和DOS。这个SQLite存储不支持写入到WebDAV文件系统。









SQLite File Size and Record Deletion

SQLite 文件大小和记录删除

Simply deleting a record from a SQLite store does not necessarily result in a reduction in the size of the file. If enough items are removed to free up a page in the database file, SQLite’s automatic database vacuuming will reduce the size of the file as it rearranges the data to remove that page. Similarly, the file size is reduced if you remove an item that itself occupies multiple pages (such as a thumbnail image).

简单删除一条记录从一个SQLite存储中不会减少文件体积大小。如果足够项目被删除去释放了在数据基础文件中的一页,SQLIte的自动数据基础真空将减少文件的大小随着它从新安排数据去移除那一页。相似的,这个文件体积被减少如果你移除一个占用了很多页的项目(比如一个缩略图)。


An SQLite file is organized as a collection of pages. The data within those pages is managed through B-trees, not as simple fixed-length records. This format is more efficient for searching and for overall storage, because it allows SQLite to optimize how it stores both data and indexes in a single file. This format is also the foundation of SQLite’s data integrity (transaction and journaling) mechanism. However, the cost of this design is that some delete operations may leave holes in the file and impact read and write performance. If you delete some data and add other data, the holes left by the deleted data may be filled by the added data, or the file may be vacuumed to compact its data, whichever SQLite considers most appropriate based on the operations you’re performing.

一个SQLite文件是作为页的集合被管理的。在哪些页里的数据通过B-输来管理,和简单固定长度记录不同。这个形式是对查找和整体存储是更高效的,因为它允许SQLite 去优化它如何在一个单独文件中存储数据和索引。这个形式也是SQLite的数据完整性机制的基础。这个设计的开销就是一些删除操作可能遗留在文件上遗留一些洞和影响读和写的执行。如果你删除一些数据和添加另外数据,被删除留下的洞可能被添加数据填充,或这个文件可能被真空去紧凑它的数据,无论如何SQLite考虑最合适的给予你执行的操作。









Configuring Save Behavior for a SQLite Store

为一个SQLite 存储配置保存行为


When Core Data saves a SQLite store, SQLite updates just part of the store file. Loss of that partial update would be catastrophic, so ensure that the file is written correctly before your application continues. Unfortunately, doing partial file updates means that in some situations saving even a small set of changes to a SQLite store can take considerably longer than saving to, say, an XML store. For example, where saving to an XML file might take less than a hundredth of a second, saving to a SQLite store may take almost half a second. This data loss risk is not an issue for XML or binary stores. Because writes to these stores are typically atomic, it is less likely that data loss involves corruption of the file, and the old file is not deleted until the new has been successfully written.

当CD保存一个SQLite 存储,SQLite只更新存储文件的一部分。部分更新的丢失将是灾难性的,所以保证在你的应用继续前保证文件被正确写入。不幸的是部分的文件更新意味着在一些场景保存甚至一个小集合的改变到一个SQLite存储能消耗更久时间比XML存储。比如,保存到一个XML文件可能消耗的时间比百分之一秒还少,保存到一个SQLite 存储可能消耗几乎半秒时间。这个数据丢失风险对XML或二进制存储不是问题。因为写入到哪些存储是典型原子性的,

数据丢失包含文件的腐化是很少可能的,老文件不会删除直到新的已经被成功写入。


IMPORTANT

In OS X the fsync command does not guarantee that bytes are written, so SQLite sends a F_FULLFSYNC request to the kernel to ensure that the bytes are actually written through to the drive platter. This request causes the kernel to flush all buffers to the drives and causes the drives to flush their track caches. Without this, there is a significantly large window of time within which data will reside in volatile memory. If system failure occurs you risk data corruption.

在OS X这个fsync 命令不会保证字节被写入,所以SQLite发送一个F_FULLSFYNC请求到内核去取保指节被真正通过驱动盘写入。这个请求造成内核去刷新所有缓存到驱动盘和造成驱动去刷新它们追踪的高速缓存。没有这个,没有这个,将有一个显著大的时间窗口在其中数据将驻留在不稳定的内存中。如果系统失败发生你磁盘数据腐坏。









Changing a Store’s Type and Location

改变一个存储的类型和位置

You can migrate a store from one type or location to another (for example, for a Save As operation) using the NSPersistentStoreCoordinator method migratePersistentStore:toURL:options:withType:error:. After invocation of this method, the original store is removed from the coordinator; thus the persistent store is no longer a useful reference. The method is illustrated in the following code fragment, which shows how you can migrate a store from one location to another. If the old store type is XML, then the example also converts the store to SQLite.

你能迁移一个存储从一个类型或位置到另一个(比如,一个保存作为一个操作)使用NSPersistentStoreCoordinator方法migratePersistentStore:toURL:options:withType:error:.在调用这个方法以后,这个原始存储被从协调器中移除;因此这个持久存储不再是一个有用的引用。这个方法在下面的代码片段被阐明,展示你如何能迁移一个存储从一个位置到另一个。如果老存储的类型是XML,这个例子也能转化那个存储到SQLite.


OBJECTIVE-C

NSPersistentStoreCoordinator *psc = [[self managedObjectContext] persistentStoreCoordinator];

NSURL *oldURL = <#URL identifying the location of the current store#>;

NSURL *newURL = <#URL identifying the location of the new store#>;

NSError *error = nil;

NSPersistentStore *xmlStore = [psc persistentStoreForURL:oldURL];

NSPersistentStore *sqLiteStore = [psc migratePersistentStore:xmlStore

    toURL:newURL

    options:nil

    withType:NSSQLiteStoreType

    error:&error];

SWIFT

guard let psc = managedObjectContext.persistentStoreCoordinator else {

    fatalError("Failed to load persistent store")

}

let oldURL = NSURL(fileURLWithPath: "oldURL")

let newURL = NSURL(fileURLWithPath: "newURL")

guard let xmlStore = psc.persistentStoreForURL(oldURL) else {

    fatalError("Failed to reference old store")

}

do {

    try psc.migratePersistentStore(xmlStore, toURL: newURL, options:nil, withType:NSSQLiteStoreType)

} catch {

    fatalError("Failed to migrate store: \(error)")

}


To migrate a store, Core Data:

去迁移一个存储,CD:

1 Creates a temporary persistence stack. 

创建一个暂时持久栈。

2 Mounts the old and new stores. 

计算老和新存储。

3 Loads all objects from the old store. 

从老存储中加载所有的对象

4 Migrates the objects to the new store.
The objects are given temporary IDs, then assigned to the new store. The new store then saves the newly assigned objects (committing them to the external repository). 

迁移对象到新存储

对象被给定临时IDs,然后赋值到新存储。

这个新存储然后保存新赋值对象(提交它们到额外仓库中)。

5 Informs other stacks that the object IDs have changed (from the old to the new stores), which keeps the stack running after a migration. 

通知其他栈对象IDs已经被改变(从久存储到新存储),这保持在迁移以后栈的运行。

6 Unmounts the old store. 

卸载老存储

7 Returns the new store. 

返回新存储。


An error can occur if:

一个错误能发生:

You provide invalid parameters to the method 

如果你提供一个不可用的参数给方法

Core Data cannot add the new store 

CD不能添加新存储

Core Data cannot remove the old store 

CD不能移除旧存储



In the latter two cases, you get the same errors that you would get if you called addPersistentStore: or removePersistentStore: directly. If an error occurs when the store is being added or removed, treat this as an exception, because the persistence stack is likely to be in an inconsistent state.

If a failure occurs during the migration itself, instead of an error you get an exception. In these cases, Core Data unwinds cleanly and there should be no repair work necessary. You can examine the exception description to determine what went wrong — possible errors range widely from "disk is full" and "permissions problems" to "The SQLite store became corrupted" and "Core Data does not support cross store relationships”.

在后面两个场景,你获得同样如果你直接调用addPersistentStore:或removePersistentStore就会发生的错误。当存储被添加或移除如果一个错误发生,把这当作一个意外,因为持久栈就像在一个非持续状态。如果一个错误发生在迁移它自己期间,而不是错误你获得一个意外。在哪些场景中,CD完全的放松那里应该没有必要的修复工作。你能检查意外描述去决定发生什么错误-可能错误范围从“磁盘已满”和“权限问题”到“SQLite存储变得损坏”和“CD不支持跨存储关系”。


Associating Metadata with a Store

把存储和原数据关联

A store’s metadata provides additional information about the store that is not directly associated with any of the entities in the store.

一个存储的元数据提供附加的 关于没有直接关联到在存储中任何实体的 存储的 信息。

The metadata is represented by a dictionary. Core Data automatically sets key-value pairs to indicate the store type and its UUID. You can create additional custom keys for your application, or provide a standard set of keys such as kMDItemKeywords to support Spotlight indexing (if you also write a suitable importer).

元数据被通过一个字典呈现。CD自动设置键-值对去标示存储类型和它的UUID。你能创建附近自定义键为你的应用,或提供一套标准比如KMDItemKeywords去支持聚光灯索引(如果你也写了一个合适的输入)。







Be careful about what information you put into metadata. Spotlight imposes a limit to the size of metadata and replicating an entire document in metadata is probably not useful. However, if you create a URL to identify a particular object in a store (using URIRepresentation), the URL may be useful to include as metadata.

小心你放入到数据元中的信息。聚光灯添加一个元数据的大小施加一个限制和替换在元数据中的一个整个文档是没有用的。然而,如果你创建一个URL去定义一个特别对象在存储中(使用URIRepresentation), 这个URL去作为数据元包含可能有效的。


Getting the Metadata

获取数据元

There are two ways to get the metadata for a store:

有两个方式去为一个存储获取数据元

Given an instance of a persistent store, get its metadata using the NSPersistentStoreCoordinator instance method metadataForPersistentStore:

给一个持久存储的一个实例,获取它的数据元使用 NSPersistentStoreCoordinator实例方法metadataForPersistentStore:.


Retrieve metadata from a store without the overhead of creating a persistence stack by using the NSPersistentStoreCoordinator class method, metadataForPersistentStoreOfType:URL:error:


从一存储获取数据元而不用多次使用NSPersistentStoreCoordinator类方法metadataForPersistentStoreOfType:URL:error:创建持久栈。


There is an important difference between these approaches. The instance method, metadataForPersistentStore:, returns the metadata as it currently is in your program, including any changes that may have been made since the store was last saved. The class method, metadataForPersistentStoreOfType:URL:error:, returns the metadata as it is currently represented in the store itself. If there are pending changes to the store, the returned value may therefore be out of sync.

在哪些方法之间有很重要的不同。这个实例方法, metadataForPersistentStore:返回元数据是它当前在你程序中的,包含从上次存储开始任何可能已经创建的改变。这个类方法,metadataForPersistentStoreOfType:URL:error:,返回元数据是它当起呈现在存储中的自己。如果那里有未决定的改变到存储。返回的数据可能因此不是同步的。

Setting the Metadata

设置元数据

There are two ways you can set the metadata for a store:

你能为一个存储设置数据元的方法有两个:

Given an instance of a persistent store, set its metadata using the NSPersistentStoreCoordinator instance method, setMetadata:forPersistentStore:

给定一个持久存储的一个实例,设置它的数据元使用NSPersistentStoreCoordinator实例方法,setMetadata:forPersistentStore:


Set the metadata without the overhead of creating a persistence stack by using the NSPersistentStoreCoordinator class method, setMetadata:forPersistentStoreOfType:URL:error:

设置数据元而不用过度创建一个持久栈通过使用NSPersistentStoreCoordinator类方法,setMetadata:forPersistentStoreOfType:URL:error:.


There is again an important difference between these approaches. If you use setMetadata:forPersistentStore:, you must save the store (through a managed object context) before the new metadata is saved. If you use setMetadata:forPersistentStoreOfType:URL:error:, however, the metadata is updated immediately, and the last-modified date of the file is changed.

哪些方法之间有很多不同。如果你使用setMetadata:forPersistentStore:,你必须保存存储(通过一个管理对象上下文)在新数据元被保存之前。如果你使用setMetadata:forPersistentStoreOfType:URL:error:,然而这个数据被立即更新,文件的 上次-改变时间被改变。














This difference has particular implications if you use  NSPersistentDocument  on OS X. If you update the metadata using setMetadata:forPersistentStoreOfType:URL:error: while you are actively working on the persistent store (that is, while there are unsaved changes), then when you save the document you will see a warning, “This document's file has been changed by another application since you opened or saved it.” To avoid this, you should instead use setMetadata:forPersistentStore:. To find the document’s persistent store, you typically ask the persistent store coordinator for its persistent stores (persistentStores), and use the first item in the returned array to set the metadata. When you use setMetadata:forPersistentStoreOfType:URL:error: the action is treated as if the change occurred externally to your Core Data stack.

如果你用NSPersistentDocument在OS X上特别的影响。如果你更新数据元使用setMetaData:forPersistentStoreOfType:URL:error:当你积极的工作在持久存储(也就是当有未保存的改变时),然后当你保存文档你将看到一个警告,“这个文档的文件已经被另一个应用改变从你打开或保存它。”去避免这个你应该使用setMetadata:forPersistentStore:.去找到文档的持久存储,你询问持久存储协调器他的持久存储(persistentStores),和使用在返回数组中的第一个项目去设置数据元。当你使用setMetadata:forPersistentStoreOfType:URL:error这个动作被当作就像改变额外的发生到你的CD栈上。


Because Core Data manages the values for NSStoreTypeKey and NSStoreUUIDKey in the same metadata, make a mutable copy of any existing metadata before setting your own keys and values, as illustrated in the following code fragment:

因为CD栈管理值为NSStoreTypeKey和NSStoreUUIDKey在相同的数据元中,做一个任何存在数据元可变的拷贝在你设置你自己的键值之前,就想下面的代码片段阐明的那样:


OBJECTIVE-C

NSURL *url = [NSURL fileURLWithPath:@"url to store"];

NSPersistentStore *store = [self.managedObjectContext.persistentStoreCoordinator persistentStoreForURL:url];

NSMutableDictionary *metadata = [[store metadata] mutableCopy];

metadata[@"MyKeyWord"] = @"MyStoredValue";

[store setMetadata:metadata];

SWIFT

let url = NSURL(fileURLWithPath: "url to store")

guard let store = managedObjectContext.persistentStoreCoordinator?.persistentStoreForURL(url) else {

    fatalError("Failed to retrieve store from \(url)")

}

var metadata = store.metadata

metadata["MyKeyWord"] = "MyStoredValue"

store.metadata = metadata








Concurrency

并发

Concurrency is the ability to work with the data on more than one queue at the same time. If you choose to use concurrency with Core Data, you also need to consider the application environment. For the most part, AppKit and UIKit are not thread-safe. In OS X in particular, Cocoa bindings and controllers are not threadsafe — if you are using these technologies, multithreading may be complex.

并发是去使用数据在不止一个队列中在相同的时间去工作。如果你选择去使用CD去使用并非,你也需要去考虑应用环境。对大多数的部分,APPKit 和UIKit 不是 线程-安全。在OS X特殊点,Cocoa 绑定和控制是非 线程安全-如果你使用哪些技术,多线程可能很复杂。


Core Data, Multithreading, and the Main Thread

CD,多线程,和主线程

In Core Data, the managed object context can be used with two concurrency patterns, defined by NSMainQueueConcurrencyType and NSPrivateQueueConcurrencyType.

NSMainQueueConcurrencyType is specifically for use with your application interface and can only be used on the main queue of an application.

在CD中,这个管理对象上下文能被两个并发部分使用,通过NSMainQueueConcurrencyType和NSPrivateQueueConcurrencyType定义。NSMainQueueConcurrencyType是专门为你应用界面使用的和只能被使用在应用的主队列中。







The NSPrivateQueueConcurrencyType configuration creates its own queue upon initialization and can be used only on that queue. Because the queue is private and internal to the NSManagedObjectContext instance, it can only be accessed through the performBlock: and the performBlockAndWait: methods.

这个NSPrivateQueueConcurrencyType配置创建它自己的队列在初始时和只能被在那个队列上使用。因为队列是私有的和在NSManagedObjectContext实例内部,它只能通过performBlock:和performBlockAndWait:方法访问。


In both cases, the initialization of the NSManagedObjectContextinstance is the same:

在所有情况,这个NSManagedObjectContext实例的初始化是相同的:

OBJECTIVE-C

NSManagedObjectContext *moc = [[NSManagedObjectContext alloc] initWithConcurrencyType:<#type#>];

SWIFT

let moc = NSManagedObjectContext(concurrencyType:<#type#>)

The parameter being passed in as part of the initialization will determine what type of NSManagedObjectContext is returned.

这个参数被作为初始化的一部份被传入将决定什么类型的NSManagedObjectContext被返回。


Using a Private Queue to Support Concurrency

使用私有队列去支持并发

In general, avoid doing data processing on the main queue that is not user-related. Data processing can be CPU-intensive, and if it is performed on the main queue, it can result in unresponsiveness in the user interface. If your application will be processing data, such as importing data into Core Data from JSON, create a private queue context and perform the import on the private context. The following example shows how to do this:

通常避免在不是 用户-相关的 主队列上处理数据。数据处理能是CPU-密集型的,和如果被在主线程执行,它能在用户界面导致反应迟钝。如果你的应用将处理数据,比如从JSON导入数据到CD中,创建一个私有队列上下文和执行导入操作在私有上下文。下面的例子展示了如何去这样做:

OBJECTIVE-C

NSArray *jsonArray = …; //JSON data to be imported into Core Data

NSManagedObjectContext *moc = …; //Our primary context on the main queue

 

NSManagedObjectContext *private = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];

[private setParentContext:moc];

 

[private performBlock:^{

    for (NSDictionary *jsonObject in jsonArray) {

        NSManagedObject *mo = …; //Managed object that matches the incoming JSON structure

        //update MO with data from the dictionary

    }

    NSError *error = nil;

    if (![private save:&error]) {

        NSLog(@"Error saving context: %@\n%@", [error localizedDescription], [error userInfo]);

        abort();

    }

}];

SWIFT

let jsonArray = … //JSON data to be imported into Core Data

let moc = … //Our primary context on the main queue

 

let privateMOC = NSManagedObjectContext(concurrencyType: .PrivateQueueConcurrencyType)

privateMOC.parentContext = moc

 

privateMOC.performBlock {

    for jsonObject in jsonArray {

        let mo = … //Managed object that matches the incoming JSON structure

        //update MO with data from the dictionary

    }

    do {

        try privateMOC.save()

    } catch {

        fatalError("Failure to save context: \(error)")

    }

}











In this example an array of data has been originally received as a JSON payload. You then create a new NSManagedObjectContext that is defined as a private queue. The new context is set as a child of the main queue context that runs the application. From there you call performBlock: and do the actual NSManagedObject creation inside of the block that is passed to performBlock:. Once all of the data has been consumed and turned into NSManagedObject instances, you call save on the private context, which moves all of the changes into the main queue context without blocking the main queue.

在这个例子一个数据数组原本已经获取一个JSON负载。你然后创建一个被定义作为一个私有队列的NSManagedObjectContext。这个新上下文被设置作为运行这个应用的主队列的子队列。从你调用performBlock:和开水真正NSManagedObject创建在被传递到performBlock闭包的内部。一旦所有的数据消化和抓换到NSManagedObject实例,你调用在私有上下文调用保存,这一动所有的改变到主队列上下文而不阻塞主队列。


Passing References Between Queues

在队列之间传递引用

NSManagedObject instances are not intended to be passed between queues. Doing so can result in corruption of the data and termination of the application. When it is necessary to hand off a managed object reference from one queue to another, it must be done through NSManagedObjectID instances.

NSManagedObject实例不打算在队列之间传递。这样做能造成数据的损伤和杀死应用。当必须摒弃 传递一个管理对象引用从一个队列到另一个,必须通过NSManagedObjectID实例。


You retrieve the managed object ID of a managed object by calling the objectID method on the NSManagedObject instance.

你通过调用NSManagedObject实例的objectID方法获取一个管理对象的管理对象ID。












Performance

执行

Core Data is a rich and sophisticated object graph management framework capable of dealing with large volumes of data. The SQLite store can scale to terabyte-sized databases with billions of rows, tables, and columns. Unless your entities themselves have very large attributes or large numbers of properties, 10,000 objects is considered a fairly small size for a data set. When working with large binary objects, review Binary Large Data Objects (BLOBs).

CD是丰富的和精密的的对象图表管理框架能够处理大体积的数据。SQLite存储能扩展大小到T字节大小使用数十亿的行,表,和列。除非你的实体自身有很大属性或大数据的属性。1万个对象被认为是一个很小的数据集合。当使用大二进制对象,参见Binary Large Data Objects(BLOBs).


Overhead versus Functionality in Core Data

在CD中的开销和功能

For a very simple application Core Data adds some overhead — compare a vanilla Cocoa document-based application with a Cocoa Core Data document-based application. However, even a simple Core Data-based application supports undo and redo, validation, object graph maintenance, and provides the ability to save objects to a persistent store. If you implemented this functionality yourself, it is quite likely that the overhead would exceed that imposed by Core Data. As the complexity of an application increases, the proportionate overhead that Core Data imposes typically decreases. At the same time the benefit typically increases. Implementing and supporting undo and redo in a large application, for example, is usually difficult.

对一个非常小应用CD添加一些开销—对比一个普通Cocoa 文档-基础 应用和一个Cocoa CD 文档-基础应用。然而,甚至一个简单CD-基础应用支持撤销和重做,验证,对象图表保持,和停供能力去保持对象到一个持久存储中。如果你自己实现这个功能,就好像这个开销超过CD施加的开销。随着一个应用的复杂的增加,CD施加的开销的比例也显著的减少。同时受益性显著的增加。实现和支持撤销和重组在一个大应用中,举例是通常困难的。


NSManaged Object Storage Mechanism

管理对象存储机制

NSManagedObject uses an internal storage mechanism for data that is highly optimized. In particular, it leverages the information about the types of data that is available through introspecting the model. When you store and retrieve data in a manner that is compliant with key-value coding and key-value observing, it is likely that using NSManagedObject will be faster than any other storage mechanism — including for the simple get/set cases. In a modern Cocoa application that leverages Cocoa bindings, given that Cocoa bindings are reliant upon key-value coding and key-value observing, it would be difficult to build a raw data storage mechanism that provides the same level of efficiency as Core Data.

管理对象使用一个高度优化的内部存储机制为数据。它通过内观模型充分利用关于可用的数据的类型的信息。当你存储和获取数据用一种兼容了 键-值 和键-值观测 的方式,酒好像使用NSManagedObject将比任何其他存储机制更快-包括简单的获取/设置场景。在一个现代的利用了Cocoa绑定的Cocoa应用,该那个Cocoa绑定依赖于 键-值 编码和 键-值观察,去建造一个能提供和CD相同效率层级的原始数据存储机制将是困难的。


Fetching Managed Objects

获取管理对象

Each round trip to the persistent store (each fetch) incurs an overhead, both in accessing the store and in merging the returned objects into the persistence stack. Avoid executing multiple requests if you can instead combine them into a single request that will return all the objects you require. You can also minimize the number of objects you have in memory.

每次往返到持久存储(每次获取)导致开销,不管是访问存储和合并返回对象到持久栈中。避免执行多次请求如果你能把它们组合到一个将返回你查询所有数据单独的请求。你也能最小化你在内存中的对象数量。










Fetch Predicates

How you use predicates can significantly affect the performance of your application. If a fetch request requires a compound predicate, you can make the fetch more efficient by ensuring that the most restrictive predicate is the first, especially if the predicate involves text matching (contains, endsWith, like, and matches). Correct Unicode searching is slow. If the predicate combines textual and nontextual comparisons, then it is likely to be more efficient to specify the nontextual predicates first, for example, (salary > 5000000) AND (lastName LIKE 'Quincey') is better than (lastName LIKE 'Quincey') AND (salary > 5000000). For more about creating predicates, see Predicate Programming Guide.

获取谓词

你如何使用谓词能显著的影响你的应用的执行。如果一个获取请求查询一个复合谓词,你能通过保证最具限制性的谓词是第一个让获取更高效,特别如果谓词包含文本匹配(contains,endsWith,like,和matches).正确的单一编码搜索是慢的。如果谓词组合了文本和非文本的对比,然后去明确非文本谓词首先是更高效的,比如(salary>5000000)AND (lastName LIKE ‘Quincey’)比(lastName LIKE ‘Quincey’)AND (salary > 5000000).更多关于创建谓词参见Predicate Programming Guide.


Fetch Limits

获取限制

You can set a limit to the number of objects a fetch will return using the method setFetchLimit:, as shown in the following example:

你能使用方法setFetchLimit:设置一个获取将返回的对象的数量的限制,想下面显示的那样:

OBJECTIVE-C

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

[request setFetchLimit:100];

SWIFT

let request = NSFetchRequest()

request.fetchLimit = 100

If you are using the SQLite store, you can use a fetch limit to minimize the working set of managed objects in memory, and so improve the performance of your application.

如果你正在使用SQLite存储,你能使用一个获取限制去最小化工作在内存中的管理对象集合,提高应用的执行效率。




If you need to retrieve many objects, you can make your application appear more responsive by executing multiple fetches. In the first fetch, you retrieve a comparatively small number of objects—for example, 100—and populate the user interface with these objects. You then execute subsequent fetches to retrieve the complete result set via the fetchOffset method.

如果你需要去获取很多对象,你能让你的应用表现反应更加灵敏通过执行很多获取。在第一个获取中,你取得一个相对小数量的对象-比如,100-和使用哪些对象产生用户界面。你然后通过执行fetchOffset方法以后获取去获取完成的集合。


Preventing a Fault from Firing

防止一个断层发生


Firing faults can be a relatively expensive process (potentially requiring a round trip to the persistent store), and you may wish to avoid unnecessarily firing a fault. You can safely invoke the following methods on a fault without causing it to fire: isEqual:, hash, superclass, class, self, zone, isProxy, isKindOfClass:, isMemberOfClass:, conformsToProtocol:, respondsToSelector:, description, managedObjectContext, entity, objectID, inserted, updated, deleted, and isFault.

触发断层能是相对昂贵的过程(潜在需要一个到持久存储的往返),和你可能希望去避免不必要的触发断层。你能安全触发在断层上的如下方法而不用造成它去触发:isEqual:,hash,superclass,class,self,zone,isProxy,isKindOfClass:isMemberOfClass:,conformsToProtocol:,respondsToSelector:,Description,managedObjectContext,entity,objectID,inserted,updated,deleted,和isFault,


Because isEqual: and hash do not cause a fault to fire, managed objects can typically be placed in collections without firing a fault. Note, however, that invoking key-value coding methods on the collection object might in turn result in an invocation of valueForKey: on a managed object, which would fire a fault. In addition, although the default implementation of description does not cause a fault to fire, if you implement a custom description method that accesses the object’s persistent properties, this will cause a fault to fire.

Note that just because a managed object is a fault, it does not necessarily mean that the data for the object is not in memory—see the definition for isFault.

因为isEqual:和hash不会造成一个断层触发,管理对象能放置到一个集合中而不用触发一个断层。注意唤醒一个 键-值 编码 方法在一个集合对象可能导致在一个管理对象上的valuedForKey:唤醒,这将触发一个断层。作为添加,尽管默认description的实现不造成一个断层触发,如果你实现一个自定义的访问对象的持久的描述方法,这将导致一个断层的触发。注意只是因为一个管理对象是一个断层,并补是一定意外着这个对象的数据不在内存中-参见isFault的定义。


Decreasing Fault Overhead

减少断层开销

When you execute a fetch, Core Data fetches only instances of the entity you specify. In some situations (see Faulting Limits the Size of the Object Graph), the destination of a relationship is represented by a fault. Core Data automatically resolves (fires) the fault when you access data in the fault. This lazy loading of the related objects is much better for memory use, and much faster for fetching objects related to rarely used (or very large) objects. It can also, however, lead to a situation where Core Data executes separate fetch requests for a number of individual objects, which incurs a comparatively high overhead. For example, consider this model:

当你执行一个获取,CD仅仅获取你明确的实体的实例。在一些场景中(参见Faulting Limits the Size of the Object Graph),当一个关系的目的被一个断层呈现。CD自动的决定(触发)这个断层当你在断层中访问数据。这个相关数据的懒加载对内存使用很好,对获取很少用的或非常大的对象是更快的。它也能导致一个情景CD处理分开的请求是请求一批独立对象,这招致一个相对高开销。比如考虑如下模型:





 








You might fetch a number of Employees and ask each in turn for their Department's name, as shown in the following code fragment.

你可能获取一批的雇员和请求每个它们的部门的名字,


OBJECTIVE-C

NSFetchRequest * employeesFetch = [NSFetchRequest fetchRequestWithEntityName:@"Employee"];

// The request should include a predicate -- if you don't have a predicate here,

// you should probably just fetch all the Departments.

NSArray *fetchedEmployees = [moc executeFetchRequest:employeesFetch error:&error];

if (fetchedEmployees == nil) {

    NSLog(@"Error fetching: %@\n%@", [error localizedDescription], [error userInfo]);

    abort();

}

for (Employee *employee in fetchedEmployees) {

    NSLog(@"%@ -> %@ department"employee.name, employee.department.name);

}

SWIFT

let employeesFetch = NSFetchRequest(entityName: "Employee")

employeesFetch.relationshipKeyPathsForPrefetching = ["department"]

// The request should include a predicate -- if you don't have a predicate here,

// you should probably just fetch all the Departments.

do {

    let fetchedEmployees = try moc.executeFetchRequest(employeeFetch) as! [ a href="" AAAEmployeeMO /a ]

    for employee in fetchedEmployees {

        print("\(employee.name) -> \(employee.department.name)")

    }

} catch {

    fatalError("Failed to fetch employees: \(error)")

}

This code might lead to the following behavior:

Jack -> Sales [fault fires]

Jill -> Marketing [fault fires]

Benjy -> Sales

Gillian -> Sales

Hector -> Engineering [fault fires]

Michelle -> Marketing




Here, there are four round trips to the persistent store (one for the original fetch of Employees, and three for individual Departments). These trips represent a considerable overhead on top of the minimum two trips to the persistent store.

这里有四个往返到持久存储(一个是原始的雇员的获取,和三个到单独的部门)。哪些过程展示了可考虑的开销在顶部的最少两次到持久存储的访问。

There are two techniques you can use to mitigate this effect—batch faulting and prefetching.

这里有两个技术你能用来去减轻这个影响-批次断层和预获取


Batch Faulting

批断层

You can batch fault a collection of objects by executing a fetch request using a predicate with an IN operator, as illustrated by the following example. (In a predicate, self represents the object being evaluated—see Predicate Format String Syntax.)

你能批次断层一个集合的数据通过使用一个谓词和一个IN 操作 执行一个获取请求,就像下面的例子阐明的那样。(在一个谓词,自己展示对象被估值)-参见predicate Format String Syntax.)

OBJECTIVE-C

NSArray *array = @[fault1, fault2, ...];

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

SWIFT

let array = [fault1, fault2, ...]

let predicate = NSPredicate(format:"self in %@", array)

When you create a fetch request you can use the NSFetchRequest method setReturnsObjectsAsFaults: to ensure that managed objects are not returned as faults.

当你使用一个获取请求你能用这个NSFetchRequest的方法setReturnsObjectsAsFaults:去保证管理对象被作为断层被返回。











Prefetching

预获取

Prefetching is in effect a special case of batch-faulting, performed immediately after another fetch. The idea behind prefetching is the anticipation of future needs. When you fetch some objects, sometimes you know that soon after you will also need related objects which may be represented by faults. To avoid the inefficiency of individual faults firing, you can prefetch the objects at the destination.

预获取是实际上一个批-断层的特殊情况,一个接着一个去执行的。预处理后的注意是对以后需要的预期。当你获取一些对象,有时你知道不久后你将页需要相关可能被断层呈现的对象。去避免低效率的单独的断层的触发,你能预获取在目的的对象。

You can use the NSFetchRequest method setRelationshipKeyPathsForPrefetching: to specify an array of relationship key paths to prefetch along with the entity for the request. For example, given an Employee entity with a relationship to a Department entity: If you fetch all the employees, for each employee, print out the employee’s name and the name of the department. You can avoid the possibility of a fault being fired for each department instance by prefetching the department relationship. This is illustrated in the following code fragment:

你能使用NSFetchRequest 方法 setRelationshipKeyPathsForPrefetching:去明确一数组的关系key paths 还有请求的实体到预获取。比如给一个雇员实体一个关系到一个部门实体:如果你获取所有的雇员,每一个雇员打印部门的名字和部门的名字。你能避免通过预获取部门管理的每一个部门实例 一个断层被触发的可能。

OBJECTIVE-C

NSManagedObjectContext *moc = …;

NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:@"Employee"];

[request setRelationshipKeyPathsForPrefetching:@[@"department"]];

SWIFT

let moc = …

let request = NSFetchRequest(entityName:"Employee")

fetchRequest.relationshipKeyPathsForPrefetching = [“department"]








If you know something about how the data will be accessed or presented, you can further refine the fetch predicate to reduce the number of objects fetched. Note, though, that this technique can be fragile — if the application changes and needs a different set of data, then you can end up prefetching the wrong objects.

如果你知道关于数据如何呗访问或呈现的事,你能更深的优化获取谓词去减少获取对象的数量。注意,尽管,这个技术可能是脆肉的-如果应用改变和需要一个不同的集合数据,然后你能结束预获取错对象。


For more about faulting, and in particular the meaning of the value returned from isFault, see Faulting and Uniquing.

更多关于断层,在特殊情况下从isFault返回的值有什么意思,参见Faulting and Uniquing.


Reducing Memory Overhead

减少内存的开销

Sometimes you need to use managed objects on a temporary basis, for example, to calculate an average value for a particular attribute. Loading a large number of objects into memory causes your object graph, and memory consumption, to grow. You can reduce the memory overhead by refaulting individual managed objects that you no longer need, or you can reset a managed object context to clear an entire object graph. You can also use patterns that apply to Cocoa programming in general. Follow these guidelines to reduce memory overhead:

有时你需要去使用管理对象在一个临时基础上使用管理对象,比如,去计算平均值为一个指定的属性。加载很多对象到内存中造成你的对象图表,和内存消耗,增加。你能减少内存消耗通过断层你不再需要的独立管理对象,或你能再次设置一个管理对象上下文去清除整个对象图表。你也能使用应用到Cocoa 通过的编程模式。遵守指导去减少内存开销:

Refault an individual managed object using the refreshObject:mergeChanges: method for NSManagedObjectContext. Doing so clears the object’s in-memory property values thereby reducing its memory overhead. (Note that the values will be retrieved on demand if the fault is again fired — see Faulting and Uniquing.) 

再次断层一个单独管理对象使用refreshObject:mergeChanges:方法为NSManagedObjectContext.这样做清除对象的在内存中的属性值来减少它的内存消耗(注意这个值将被获取在要求中如果断层再次触发-参见Faulting and Uniquing.)





When you create a fetch request, set includesPropertyValues to NO to reduce memory overhead by avoiding creation of objects to represent the property values. You should typically only do so, however, if you are sure that either you will not need the actual property data or you already have the information in the row cache. Otherwise you will incur multiple trips to the persistent store. 

当你创建一个获取请求,设置includesPropertyValues为NO去通过避免创建对象去呈现属性值去减少内存的开销。你应该只这样做,然而,如果你确定你将不在需要真正属性值或你已经有信息在哪行的高速缓存中。否则你将招致很多到持久存储的过程。


Use the reset method of NSManagedObjectContext to remove all managed objects associated with a context, and start over as if you'd just created it. Note that managed objects associated with that context will be invalidated, and so you will need to discard any references to and refetch any objects associated with that context. 

If you iterate over a lot of objects, you may need to use local autorelease pool blocks to ensure temporary objects are deallocated as soon as possible. 

使用NSManagedObjectContext的方法reset去移动所有和这个上下相关的管理对象,和再次开始就好像你刚刚创建完它。注意和那个上下相关的对象将不可用,所以你将需要去放弃任何引用和再次获取任何和那个上下文相关的对象。如果你遍历大量对象,你可能需要去用当地的自动释放池闭包去确保临时对象被尽早的释放。


If you do not intend to use Core Data’s undo functionality, reduce your application's resource requirements by setting the context’s undo manager to nil. This may be especially beneficial for background worker threads, as well as for large import or batch operations. 

If you have lots of objects in memory, determine the owning references. Core Data does not by default keep strong references to managed objects (unless they have unsaved changes). Managed objects maintain strong references to each other through relationships, which can easily create strong reference cycles. You can break cycles by refaulting objects (again by using the refreshObject:mergeChanges: method of NSManagedObjectContext). 

如果你不用CD的撤销功能,减少你的应用的资源需求通过设置上下文的撤销管理者到nil.这个可能很有用对后台工作线程,还有大的输入或批操作。如果你有很多对象在内存中,决定拥有引用。CD不会默认保持强引用管理对象(除非它们有未保持的改变)。管理对象保持强引用到另一个通过关系,这很容易创建引用的循环。你能打破这个循环通过再次断层对象(再次使用NSManagedObjectContext的方法    refreshObject:mergeChanges:)。


Binary Large Data Objects (BLOBs)

二进制大数据对象

If your application uses Binary Large Objects (BLOBs) such as image and sound data, you need to take care to minimize overheads. Whether an object is considered small or large depends on an application’s usage. A general rule is that objects smaller than a megabyte are small or medium-sized and those larger than a megabyte are large. Some developers have achieved good performance with 10MB BLOBs in a database. On the other hand, if an application has millions of rows in a table, even 128 bytes might be a CLOB (Character Large OBject) that needs to be normalized into a separate table.

In general, if you need to store BLOBs in a persistent store, use a SQLite store. The other stores require that the whole object graph reside in memory, and store writes are atomic (see Persistent Store Types and Behaviors), which means that they do not efficiently deal with large data objects. SQLite can scale to handle extremely large databases. Properly used, SQLite provides good performance for databases up to 100GB, and a single row can hold up to 1GB (although of course reading 1GB of data into memory is an expensive operation no matter how efficient the repository).

如果你的应用使用二进制大数据对象比如图片或声音数据,你需要去考虑最小化开销。一个对象是否被认定小或大取决于一个应用的使用。一个常见规则是一个对象比一兆小是小或中等大小和哪些大于一兆是大。一些开发者能完成很好执行使用10兆二进制大数据在数据基础中。另一方面如果一个应用在一个table中有百万行。128个字节甚至都可能成为一个CLOB(字符大对象)需要去呗正常化到分开的table. 通常如果你需要去存储BLOB在一个持久存储中,使用一个SQLite能去处理非常大的数据库。其他存储需要整个对象图表在内存中,和存储写入是原子性的(参见Persistent Store Types 和Behaviors),这意味着它们不是高效处理大数据对象。SQLite能为大到100GB的数据库提供高执行,和一个单独行能处理1GB(尽管读取1GB数据到内存中是一个消耗的操作不管属性多么高效)。





A BLOB often represents an attribute of an entity—for example, a photograph might be an attribute of an Employee entity. For small to modest BLOBs (and CLOBs), create a separate entity for the data and create a to-one relationship in place of the attribute. For example, you might create Employee and Photograph entities with a one-to-one relationship between them, where the relationship from Employee to Photograph replaces the Employee's photograph attribute. This pattern maximizes the benefits of object faulting (see Faulting and Uniquing). Any given photograph is only retrieved if it is actually needed (if the relationship is traversed).

一个BLOB通常展示一个属性的一个属性-比如,一个图表可能是一个雇员实体的一个属性。为了更小的去适合BLOBs(和CLOBs),为数据创建一个分开的实体和创建一个 对一 关系在哪个属性中。比如你可能闯进雇员和图表实体使用一个 一对一 关系在它们之间,从雇员到图表取代了雇员的图标属性。这个模式最大化断层对象的利益(参见Faulting 和Uniquing).和给图表一个只能在珍重需要的时候获取(如果关系被贯穿)


It is better, however, if you are able to store BLOBs as resources on the file system, and to maintain links (such as URLs or paths) to those resources. You can then load a BLOB as and when necessary.

这样更好,然后你能够去存储BLOBs作为文件系统上的资源,和去保持链接(比如一个URLs或paths)到哪些资源。然后你能加载一个BLOB在任何需要的时候。


Analyzing Fetch Behavior with SQLite


You can use the user default com.apple.CoreData.SQLDebug to log to stderr the actual SQL sent to SQLite. (Note that user default names are case-sensitive.) For example, you can pass the following as an argument to the application:

你能使用用户默认的com.apple.CoreData.SQLDebug去打印到stderr发送到SQLite的SQL。(注意用户默认的名字是区分大小写的。)比如你能传递下面的内容作为参数到应用中:


-com.apple.CoreData.SQLDebug 1

Higher levels of debug numbers produce more information, although this is likely to be of diminishing utility.

调试数据的高层级产生更多信息,尽管这事就像是递减的效用。


The information the output provides can be useful when debugging performance problems—in particular it may tell you when Core Data is performing a large number of small fetches (such as when firing faults individually). The output differentiates between fetches that you execute using a fetch request and fetches that are performed automatically to realize faults.

输出提供的信息能很有用当调试执行问题-特殊的地方它可能高速你CD什么时候执行了很多的小获取(比如当单独的触发断层)。在你使用获取请求和自动被执行去实例断层的获取之间的输出不同点。

Analyzing Application Behavior with Instruments

使用工具分析应用行为

There are several Instruments probes specific to Core Data:

这里有一些为CD设计的工具探针

Core Data Fetches. Records invocations of executeFetchRequest:error:, providing information about the entity against which the request was made, the number of objects returned, and the time taken for the fetch. 

CD获取。executeFetchRequest:error:调用的纪录,提供被发送请求的实体的相关信息,返回对象的数据,每次请求消耗的时间

Core Data Saves. Records invocations of save: and the time taken to do the save. 

CD保存。save:调用的纪录和每次保存消耗的时间

Core Data Faults. Records information about object and relationship fault firing. For object faults, Instruments records the object being faulted; for relationship faults, it records the source object and the relationship being fired. In both cases, it records the time taken to fire the fault. 

CD断层。纪录关于对象 和 关系断层触发的纪录信息。对对象断层,工具纪录对象被断层;对关系断层,它纪录源对象和被触发的关系。在两个情景中,它纪录触发断层需要的时间。

Core Data Cache Misses. Traces fault behavior that specifically results in file system activity — the instrument indicates that a fault was fired for which no data was available — and records the time taken to retrieve the data. 

CD缓存丢失。追踪断层的导致文件系统活动个行为-工具标示这一个没有数据可用的断层被触发-和纪录获取数据需要的时间

All the instruments provide a stack trace for each event so that you can see what caused the event.

所有工具提供一个栈追中为每一个事件所依你能看到什么造成哪个事件


Additional Performance Information

添加执行信息

Like all technologies, Core Data can be abused. You still need to consider basic Cocoa patterns, such as memory management. Also consider how you fetch data from a persistent store. Take into account factors not directly related to Core Data, such as overall memory footprint, object allocations, use and abuse of other API such as the key-value technologies, and so on. If you find that your application is not performing as well as you would like, use profiling tools such as Instruments to determine where the problem lies. See Performance in the Mac Developer Library.

就像所有的技术CD能被滥用。你依旧需要去考虑疾病的Cocoa模式,比如内存管理。也考虑你如何从持久存储获取数据。计入账号因素不直接联系到CD,比如总的内存占用,对象内存分配,使用和滥用其他的接口比如键-值技术,等等。如果你发现你的应用没有你期待的执行效果,使用分析工具比如去决定问题在什么地方。参见在Mac 开发者 库中的 Performance 









Troubleshooting Core Data

CD故障排除

Core Data builds on functionality provided by other parts of Cocoa. When diagnosing a problem with an application that uses Core Data, take care to distinguish between problems that are specific to Core Data and those that stern from an error with another framework or that are architecture-related. Poor performance, for example, may not be a Core Data problem, but instead due to a failure to observe standard Cocoa techniques of memory management or resource conservation. If a user interface does not update properly, this may be due to an error in how you have configured Cocoa bindings.

CD建造的功能是由Cocoa的其他部分提供的。当你使用了一个集成了CD的应用诊断问题,小心去分辨那些和CD明确相关的问题和一定是咋其他框架中的的问题或构造相关。性能查,可能不是CD问题,而是因为没去遵守Cocoa标志内存管理或资源保护。如果一个用户界面没有更新属性,这可能是因为你如何配置Cocoa绑定出现的错误。



Object Life Cycle Problems

对象生命循环问题

Merge errors

合并错误

Problem: You see the error message, "Could not merge changes".

Cause: Two different managed object contexts tried to change the same data. This is also known as an optimistic locking failure.

Remedy: Either set a merge policy on the context, or manually (programmatically) resolve the failure. You can retrieve the currently committed values for an object using committedValuesForKeys:, and you can use refreshObject:mergeChanges: to refault the object, so that when it is next accessed, its data values are retrieved from its persistent store.

问题:你看到错误信息:“Could not merge changes”.

原因:两个相同的管理对象上下文尝试去改变相同的数据。

这也就是正相关锁失败补救:在上下文设置一个合并策略,或手工(编码方式)解决失败。你能使用committedValuesForKeys: 获取当前对象的提交值和你能用refreshObject:mergeChanges:去再次断层这个对象,所以当它下次被访问,它的数据值被从它的持久对象获取。


Assigning a managed object to a different store

赋值管理对象到不痛的存储中


Problem: You see an exception that looks similar to this example.

<NSInvalidArgumentException> [<MyMO 0x3036b0>_assignObject:toPersistentStore:]:

Can’t reassign an object to a different store once it has been saved.

问题:你看到一个和这个例子很想的异常

Cause: The object you are trying to assign to a store has already been assigned and saved to a different store.

原因:这个你尝试去赋值到存储的对象意见呗赋值和保存到了不同存储中。

Remedy: To move an object from one store to another, you must create a new instance, copy the information from the old object, save it to the appropriate store, and then delete the old instance.

修复:去移动一个对象从一个存储到另一个,你必须创建一个新实例,从旧对象中复制信息,保存它到应用的存储中,然后删除这个旧实例。


Fault cannot be fulfilled

断层不能被执行

Problem: You see the error message, "Core Data could not fulfill a fault”.

问题:你看到错误信息,”Core Data could not fulfill a fault”

Cause: The object that Core Data is trying to realize has been deleted from the persistent store.

原因:这个CD试图去现实化的对象已经被从持久存储中删除。


Remedy: Discard this object by removing all references to it.

通过异常所有到它的引用放弃这个对象


Details: This problem can occur in at least two situations:

这个问题最少会在两个情景中出现:


First:

You started with a strong reference to a managed object from another object in your application. 

开始使用一个强引用到一个管理对象另一个在你引用中的对象。

You deleted the managed object through the managed object context. 

You saved changes on the object context. 

你通过管理对象上下删除这个管理对象。保存改变在对象上下文。
At this point, the deleted object has been turned into a fault. It isn’t destroyed because doing so would violate the rules of memory management. 

到这个点,这个删除对象已经转化成一个断层。它不被销毁因为这样做违法了内存管理的规则。


Core Data will try to realize the faulted managed object but will fail to do so because the object has been deleted from the store. That is, there is no longer an object with the same global ID in the store.

CD将尝试去现实化断层管理对象但是会失败因为这个对象已经从存储中删除。也就是,也就是在存储中不再有使用这个全局ID的对象





Second:

You deleted an object from a managed object context. 

你从一个管理对象上下文删除一个对象。

The deletion failed to break all relationships from other objects to the deleted object. 

这个删除没有打破所有从其他对象到这个删除对象的关系。

You saved changes. 

你保存改变。


At this point, if you try to fire the fault of a relationship from another object to the deleted object, it may fail, depending on the configuration of the relationship, which affects how the relationship is stored.

在这个点,如果你尝试去触发从另一个对象到删除对象关系的断层,它可能失败,取决于关系的配置,这影响关系如何被保存。


The delete rules for relationships affect relationships only from the source object to other objects (including inverses). Without potentially fetching large numbers of objects, possibly without reason, there is no way for Core Data to efficiently clean up the relationships to the object.

关系的删除规则仅仅影响从源对象到其他对象的关系(保护反转关系。)不会潜在的获取大数量的对象,没有任何原因,CD不能高效的清除到对象的关系。


Keep in mind that a Core Data object graph is directional. That is, a relationship has a source and a destination. Following a source to a destination does not necessarily mean that there is an inverse relationship. So, in that sense, you need to ensure that you are properly maintaining the object graph across deletes.

记住一个CD对象图表是定向的。也就是一个关系有一个愿和一个目的。从一个源到一个目的流向不一定意味着这里有一个反转关系。所以在那个场景,你需要去保证你在删除的时候合适的保持了对象图表。


Core Data uses inverse relationships to maintain referential integrity within the data model. If no inverse relationship exists and an object is deleted, you will be required to clean up that relationship manually.

CD使用反转关系去保持引用完整性在数据模型中。如果非反转关系存在和一个对象呗删除,你将需要去手工的清除关系。




In practice, a well-designed object graph does not require much manual post-deletion clean-up. Most object graphs have entry points that in effect act as a root node for navigating the graph, and most insertion and deletion events are rooted at those nodes just like fetches. This means that delete rules take care of most of the work for you. Similarly, because smart groups and other loosely coupled relationships are generally best implemented with fetched properties, various ancillary collections of entry points into the object graph generally do not need to be maintained across deletes, because fetched relationships have no notion of permanence when it comes to objects found through the fetched relationship.

在现实中国年,一个好—设计对象图表不需要去很多手动发送删除清除。很多对象图表有就像是为了导航图表的根节点的进入点,和很多插入和删除事件扎根在那些节点就像获取。这意味则会删除规则照顾了你的大部分工作。相似的,因为小群和其他松散群关系通常最好是使用获取属性实现,很多附加的插入点到对象图表的收集通常不需要去在删除中被保持,因为获取关系没有持久的意义当是通过获取关系查找的时候












Managed object invalidated

管理对象无效

Problem: You see an exception that looks similar to this example:

<NSObjectInaccessibleException> [<MyMO 0x3036b0>_assignObject:toPersistentStore:]:

The NSManagedObject with ID:#### has been invalidated.

问题:你看到一个看着和这个例子很相似的异常


Cause: Either you have removed the store for the fault you are attempting to fire, or the managed object's context has been sent a reset message.

原因:你已经移除了你尝试去触发的断层,或管理对象上下文已经被发送了一个reset信息。


Remedy: Discard this object by removing all references to it. If you add the store again, you can try to fetch the object again.

修复:通过移除所有到它的引用去放弃这个对象。如果你再次添加存储,你能再次尝试去获取那个对象。

Class is not key-value coding-compliant

类不是键-值 编码兼容的


Problem: You see an exception that looks similar to the following example.

<NSUnknownKeyException> [<MyMO 0x3036b0> valueForUndefinedKey:]:

this class is not key value coding-compliant for the key randomKey.

你看到和下面例子相似的异常。


Cause: Either you used an incorrect key, or you initialized your managed object with init instead of initWithEntity:insertIntoManagedObjectContext:.

原因:你使用了不正确的key,或你使用init初始化你的管理对象而不是 initWithEntity:insertIntoManagedObjectContext:.


Remedy: Use a valid key (check the spelling and case carefully—also review the rules for key-value coding compliance in Key-Value Coding Programming Guide). Ensure that you use the designated initializer for NSManagedObject (see initWithEntity:insertIntoManagedObjectContext:).

修复:用一个可用的键(检查拼写和 场景注意-也查看在Key-Value Coding Programming Guide 的键-值编码标准)。确保你使用指定初始化者为NSManagedObject (参见 initWithEntity:insertIntoManagedObjectContext:).


Entity class does not respond to invocations of custom methods

实体类不响应自定义方法的调用


Problem: You define an entity that uses a custom subclass of NSManagedObject, then in code you create an instance of the entity and invoke a custom method, as illustrated in this code fragment:

问题:你使用一个自定义的NSManagedObject的子类定义一个实体,然后在代码中你创建一个实体的实例和调用自定的方法,就像下面代码中一样;


OBJECTIVE-C

NSManagedObject *entityInstance =

    [NSEntityDescription insertNewObjectForEntityForName:@"MyEntity"

            inManagedObjectContext:managedObjectContext];

[entityInstance setAttribute: newValue];

SWIFT

let entityInstance = NSEntityDescription.insertNewObjectForEntityForName("myEntity", inManagedObjectContext: moc)

entityInstance.attribute = newValue

You get a runtime error like this:

"2005-05-05 15:44:51.233 MyApp[1234] ***

    -[NSManagedObject setNameOfEntity:]: selector not recognized [self = 0x30e340]

你获得一个这样的运行时错误:


Cause: In the model, you misspelled the name of the custom class for the entity.

原因:在模型中,你把自定义的类名错拼给了实体。


Remedy: Ensure that the spelling of the custom class name in the model matches the spelling of the custom class you implement.

修复:保证在模型中在自定义类名的拼写 匹配你实现的自定义类的拼写。



Custom accessor methods are not invoked, and key dependencies are not obeyed

自定义访问方法不被调用,和键依赖项不被遵守。


Problem: You define a custom subclass of NSManagedObject for a particular entity and implement custom accessors methods (and perhaps dependent keys). At runtime, the accessor methods are not called, and the dependent key is not updated.

问题:你自定义一个NSManagedObject的子类为一个特殊实体和实现自定义访问方法(和可能独立键)。在运行时,这个访问方法没有被调用,和独立键没有被更新。


Cause: In the model, you did not specify the custom class for the entity.

Remedy: Ensure that the model specifies the custom class name for the entity rather than NSManagedObject.

因为:在模型中,你没有为实体明确自定义的类。

修复:确保模型为实体而不是NSManagedObject明确自定义类。






Problems with Fetching

获取问题


SQLite store does not work with sorting

SQLite 存储不使用排序工作


Problem: You create a sort descriptor that uses a comparison method defined by NSString, such as the following:

问题:你创建一个使用通过NSString定义的对比方法排序描述,如下描述的这样。

OBJECTIVE-C

NSSortDescriptor *mySortDescriptor = [[NSSortDescriptor alloc]

        initWithKey:@"lastName" ascending:YES

        selector:@selector(localizedCaseInsensitiveCompare:)];

SWIFT

let mySortDescriptor = NSSortDescriptor(key: "lastName", ascending: true)


You then either use this descriptor with a fetch request or as one of an array controller's sort descriptors. At runtime, you see an error message that looks similar to the following:

你使用使用一个获取请求来用这个描述或者做完一个数组集合的集合描述。在运行时,你看到一个和下面相似的错误信息。

NSRunLoop ignoring exception 'unsupported NSSortDescriptor selector:

        localizedCaseInsensitiveCompare:' that raised during posting of

        delayed perform with target 3e2e42 and selector ‘invokeWithTarget:'


Cause: Exactly how a fetch request is executed depends on the store—see Fetching Objects.

原因:一个获取是如果执行的取决于存储-参见Fetching Objects.







Remedy: If you are executing the fetch directly, do not use Cocoa-based sort operators—instead, sort the returned array in memory. If you are using an array controller, you may need to subclass NSArrayController, so that it will not pass the sort descriptors to the database but will instead do the sorting after your data has been fetched.

修复:如果你直接执行获取,不要使用Cocoa-基础集合操作-而是集合返回数组到内存中。如果你在使用一个数组控制器,你可能需要继承一个NSArrayController,这样它将不传递集合描述到数据库但是会在你的数据被获取以后执行排序工作。


Problems with Saving

保存问题


Cannot save documents because entity is null

不能保存文档因为实体为空


Problem: You have a Core Data document-based application that is unable to save. When you try to save the document you get an exception:

问题:你有一个CD 文档—基础不能保存的应用。当你尝试去保存文档你获得一个异常


Exception raised during posting of notification.  Ignored.  exception: Cannot perform operation since entity with name 'Wxyz' cannot be found



Cause: This error is emitted by an instance of NSObjectController (or one of its subclasses) that is set in Entity mode but can’t access the entity description in the managed object model associated with the entity name specified in Interface Builder. In short, you have a controller in entity mode with an invalid entity name.

原因:这个错误被一个设置在实体中的但是不能够访问描述在管理对象模型中的 实体NSObjectController的实例使用明确在交互设置中的实体名发射(或它的子类的)。也就是你有一个在实体模型中的控制器有一个不可用的实体名。


Remedy: Select in turn each of your controllers in Interface Builder, and press Command-1 to show the inspector. For each controller, ensure you have a valid entity name in the Entity name field at the top of the inspector.

修复:顺序选择在交互界面中的你的每个控制器,和按下命令-1去在检查器中显示。对每个控制器,取保有一个可用实体名在检查器上部的实体名字区域。



Exception generated in retainedDataForObjectID:withContext

在retainedDataForObjectID:withContext产生的异常


Problem: You add an object to a context. When you try to save the document you get an error that looks like this:

问题:你添加一个对象到一个上下文,当你尝试去保存文档你获取一个这样的错误:

[date] My App[2529:4b03] cannot find data for a temporary oid: 0x60797a0 <<x-coredata:///MyClass/t8BB18D3A-0495-4BBE-840F-AF0D92E549FA195>x-coredata:///MyClass/t8BB18D3A-0495-4BBE-840F-AF0D92E549FA195>

This exception is in -[NSSQLCore retainedDataForObjectID:withContext:], and the backtrace looks like this:

#1    0x9599a6ac in -[NSSQLCore retainedDataForObjectID:withContext:]

#2    0x95990238 in -[NSPersistentStoreCoordinator(_NSInternalMethods) _conflictsWithRowCacheForObject:andStore:]

#3    0x95990548 in -[NSPersistentStoreCoordinator(_NSInternalMethods) _checkRequestForStore:originalRequest:andOptimisticLocking:]

#4    0x9594e8f0 in -[NSPersistentStoreCoordinator(_NSInternalMethods) executeRequest:withContext:]

#5    0x959617ec in -[NSManagedObjectContext save:]



The call to _conflictsWithRowCacheForObject: is comparing the object you're trying to save with its last cached version from the database. Basically, it's checking to see if any other code (thread, process, or just a different managed object context) changed this object without your knowledge.

Core Data does not do this check on newly inserted objects because they could not have existed in any other scope. They haven't been written to the database yet.

这个调用到_conflictsWithRowCacheForObject:对比你尝试去保存的对象和它上次从数据库缓存的版本。基本上,他查看如果其他的代码(线程,进程,或只是一个不同的管理对象上写文)改变这个对象在你不知道情况下。CD不会在新插入对象上做这个检查因为它们不可能在任何其他范围已经存在。它们还没有被写入到数据库。


Cause: You may have forced a newly inserted object to lose its inserted status and then changed or deleted it. This could happen if you passed a temporary object ID to objectWithID:. You may have passed an inserted object to another managed object context.

原因:你可能已经强制一个新的被插入对象丢失他的插入状态和然后改变和删除它。这个可能发生如果你传递一个临时对象ID到objectWithID:.你可能已经传递一个插入对象到另一个管理对象上下文。

Remedy: There are a number of possible remedies, depending on the root cause:

这里有一拼可能处理措施,取决于根原因:

Do not pass an inserted (not yet saved) object to another context. Only objects that have been saved can be passed between contexts. 

Do not invoke refreshObject: on a newly-inserted object. 

Do not make a relationship to an object that you never insert into the context. 

不要传递一个被插入(还没有保存)对象到另一个上下文。只有已经被保存的对象能被在上下文之间传递。不要调用refreshObjct:在一个新的-插入的对象。不要制造关系到一个你永远不会插入到上下文的对象。


Ensure that you use the designated initializer for instances of NSManagedObject

保证你使用了指定初始化者为NSManagedObject 的实例。


Before you save (frame #5 in the stack trace), make sure that the context’s updatedObjects and deletedObjects sets should only have members whose object ID returns NO from isTemporaryID.

在你保存以前(frame #5 在栈追寻),确保上下文的updatedObjects和deletedObjects集合应该只有它从isTemporaryID返回的的对象ID是NO的成员。









Debugging Fetching

调试获取

Use the user default com.apple.CoreData.SQLDebug setting to log to stderr the actual SQL statement sent to SQLite. (Note that user default names are case sensitive.) For example, you can pass the following as an argument to the application:

使用用户默认com.apple.CoreData.SQLDebug设置去打印到控制台真正的SQL发送给SQLite的语句(注意用户默认名字是区分大小写的。)比如,你能传递如下的值作为一个参数给你的引用


-com.apple.CoreData.SQLDebug 1

Higher levels of debug numbers produce more information, although using higher numbers is likely to be of diminishing utility.

高级别的调试产生更多信息尽管使用更高数字可能是递减的效果,


The information the output provides can be useful when debugging performance problems—in particular it may tell you when Core Data is performing a large number of small fetches (such as when firing faults individually). Like file I/O, executing many small fetches is expensive compared to executing a single large fetch. For examples of how to correct this situation, see Preventing a Fault from Firing.

输入提供的信息能有用当调试执行问题-在特殊中它可能告诉你CD什么在执行很大数量的小获取(比如单独触发断层的时候)。就像I/O,执行很多小获取是昂贵的相比于执行一个单独的大获取。如何去纠正这个场景,参见Preventing a Fault From Firing.



IMPORTANT

Using com.apple.CoreData.SQLDebug for reverse engineering to facilitate direct access to the SQLite file is not supported. It is exclusively a debugging tool.

As this is for debugging, the exact format of the logging is subject to change without notice. You should not, for example, pipe the output into an analysis tool with the expectation that it will work on all OS versions.传输输出到一个分析工具

使用 com.apple.CoreData.SQLDebug 为了逆向工程去方便直接访问到SQLite 文件是不支持的。这是一个唯一的调试工具。这是唯一的一个调试工具。因为这个是用来调试的准确的输出格式在不通知你会发生改变。你不应该传输输出结果到一个使用将在所有OS版本上工作的期望的分析工具






Managed Object Models

管理对象模型


Your application generates the message "+entityForName: could not locate an NSManagedObjectModel”

你的应用产生信心“+entityForName”:不能在NSManagedObjectModel中定位


Problem: The error states clearly the issue—the entity description cannot find a managed object model from which to access the entity information.

问题:错误说明的问题是-这个实体描述不能找到一个管理对象模型从这个管理对象模型去访问实体信息。


Cause: The model may not be included in your application resources. You may be trying to access the model before it has been loaded. The reference to the context may be nil.

原因:这个模型可能不被包含在你的应用资源中。你可能尝试去访问这个模型在它被加载之前。到这个上下文的引用可能是空的。


Remedy: Be sure that the model is included in your application resources and that the corresponding project target option in Xcode is selected.

The class method you invoked requires an entity name and a managed object context, and it is through the context that the entity gets the model. Basically, the core data stack looks like:

managed object context ---> persistent store coordinator ---> managed object model

修复:保证这个模型被包含在你的应用的资源和对应的项目目标选项在Xcode是可选的。你调用的类方法需要一个实体名字和一个管理上下文,和实体获得模型是通过上下文去获得的。基本上:CD栈看起来像:

管理对象上下文—>持久存储协调器器—>管理对象模型

If the managed object model cannot be found, make sure of the following:

如果管理对象模型不能被找到,保证下面的东西:

The managed object context is not nil

管理对象上下文不为空。




If you are setting the reference to the context in a
.nib file, make sure the appropriate outlet or binding is set correctly. 

如果你设置到上下文的引用是一个。nib文件,确保相关的外置和绑定被设置正确。

If you are managing your own Core Data stack, make sure that the managed object context has an associated coordinator (setPersistentStoreCoordinator: after allocating). 

如果你管理你自己的CD栈,确保管理对象上下文有配合的协调器(setPersistentStoreCoordinator: 在分配以后)

The persistent store coordinator has a valid model. 

持久存储协调器有一个可用的模型。

Bindings Integration

绑定整合

Many problems relating to bindings are not specific to Core Data, and are discussed in target=Troubleshooting Cocoa Bindings  . This section describes some additional problems that could be caused by the interaction of Core Data and bindings.

很多问题是和绑定相关的而没有明确是CD的原因,在Cocoa绑定中被讨论。这个章节描述一些附加可能被CD和绑定的交互造成的问题。


Cannot access contents of an object controller after a nib is loaded

在一个nib被加载以后不能访问一个对象控制器的内容


Problem: You want to perform an operation with the contents of an object controller (an instance of NSObjectController , NSArrayController , or NSTreeController) after a .nib file has been loaded, but the controller's content is nil.

问题:你想使用一个对象控制器(NSObjectController, NSArrayController,或NSTreeController)的内容去执行一个操作在一个nib文件已经被加载以后,但是控制器的内容是空的。


Cause: The controller's fetch is executed as a delayed operation performed after its managed object context is set (by nib loading)—the fetch therefore happens after awakeFromNib and windowControllerDidLoadNib:.

原因:控制器的获取被执行作为一个延时操作在管理对象上下文被设置以后(通过nib 加载)-这个获取发生在awakeFromNib和windowControllerDidLoadNib:.之后


Remedy: Execute the fetch manually with fetchWithRequest:merge:error:. See Using Core Data with Cocoa Bindings.

修复:手工执行fetchWithRequest:merge:error:.参见Using Core Data with Cocoa Bindings.


Cannot create new objects with array controller

不能使用数组控制器创建新对象


Problem: You cannot create new objects using an NSArrayController. For example, when you click the button assigned to the add: action, you get an error similar to the following:

问题:你不能使用一个NSArrayController去创建新对象。比如,当你点击按钮赋值到添加动作,你获取一个和下面相似的错误。

2005-05-05 12:00:)).000 MyApp[1234] *** NSRunLoop

ignoring exception 'Failed to create new object' that raised

during posting of delayed perform with target 123456

and selector ‘invokeWithTarget:'


Cause: In your managed object model, you may have specified a custom class for the entity, but you have not implemented the class.

原因:在你的管理对象模型中,你可能为实体明确一个自定义类,但是你没有实现自定义类。


Remedy: Implement the custom class, or specify that the entity is represented by NSManagedObject.

修复:实现自定义类,或明确被这个实体被NSManagedObject呈现。


A table view bound to an array controller doesn't display the contents of a relationship

一个被绑定到一个数组控制器上的table view 不显示 一个关系的内容:

Problem: You want to display the contents of a relationship in a table view bound to an array controller, but nothing is displayed and you get an error similar to the following:

问题:你想去展示关系的内容在一个绑定到数组控制器的table view 中,但是什么都没有显示和你得到和下面相似的一个错误

2005-05-27 14:13:39.077 MyApp[1234] *** NSRunLoop ignoring exception

'Cannot create NSArray from object <_NSFaultingMutableSet: 0x3818f0> ()

of class _NSFaultingMutableSet - consider using contentSet

binding instead of contentArray binding' that raised during posting of

delayed perform with target 385350 and selector 'invokeWithTarget:'

Cause: You bound the controller's contentArray binding to a relationship. Relationships are represented by sets.

原因你绑定控制器的contentArray绑定到一个关系。关系通过set呈现。


Remedy: Bind the controller's contentSet binding to the relationship.

修复:绑定控制器的contentSet绑定到关系。


A new object is not added to the relationship of the object currently selected in a table view

一个新对象没有被添加到当在table view中的对象的关系上。

Problem: You have a table view that displays a collection of instances of an entity. The entity has a relationship to another entity, instances of which are displayed in a second table view. Each table view is managed by an array controller. When you add new instances of the second entity, they are not added to the relationship of the currently selected instance of the first.

问题:你有一个table view 它展示一个一个实体的实例的集合。这个实体有一个关系到另一个实体,

Cause: The two array controllers are not related. There is nothing to tell the second array controller about the first.

原因:这两个数组控制器没有被联系。第一个控制数组控制器没有可以高速第二个数组控制器的。

Remedy: Bind the second array controller's contentSet binding to the key path that specifies the relationship of the selection in the first array controller. For example, if the first array controller manages the Department entity, and the second array controller manages the Employee entity, then the contentSet binding of the second array controller should be [Department Controller].selection.employees.

修复:绑定第二个数组控制器的contentSet到 明确在第一个数组控制器中被选项的关系的key path。比如,如果第一个数组控制器管理部门实体,和第二个数组控制器管理雇员实体,然后这个第二个数组控制器的绑定contentSet应该是[Department Controller].selection.employees.




Table view or outline view contents not kept up-to-date when bound to an NSArrayController or NSTreeController object

table view 或大纲视图内容没有保持更新当绑定到一个NSArrayController 或 NSTreeController 对象


Problem: You have a table view or outline view that displays a collection of instances of an entity. As new instances of the entity are added and removed, the table view is not kept in sync.

问题:你有一个table view 或展示一个实体的实例的集合的大纲视图。一个实体的新实例被添加和删除,这个table view  美哟保持同步。


Cause: If the controller's content is an array that you manage yourself, then it is possible you are not modifying the array in a way that is KVO-compliant.

If the controller's content is fetched automatically, then you have probably not set the controller to "Automatically prepare content."

Alternatively, the controller may not be properly configured.

原因:如果控制器的内容是一个你通知你自己的数组,然后你没有使用KVO-遵守的方式去修改数组。如果控制器的内容被自动获取,然后你可能是没有设置控制器到“Automatically prepare content.” 还可能,控制器没有被合适的配置。


Remedy: If the controller's content is a collection that you manage yourself, then ensure you modify the collection in a way that is KVO-compliant. See target Troubleshooting Cocoa Bindings  .

修复:如果控制器的内容是一个你通知你自己的集合,然后确保你修改这个集合在遵守KVO-编译的方式。参见target Troubleshooot Cocoa Bindings.


If the controller's content is fetched automatically, set the "Automatically prepares content" switch for the controller in the Attributes inspector in Interface Builder (see also automaticallyPreparesContent). Doing so means that the controller will track inserts into and deletions from its managed object context for its entity.

如果控制器的内容被自动获取,在交互界面 建设(参见 automaticallyPrepareContent)的 属性检查器中设置这个“Automatically prepares content” 开关为控制器.这样做意外这个控制器将追寻从它的管理对象上下文插入或删除。

Also check to see that the controller is properly configured (for example, that you have set the entity correctly).

也去检查控制器被合适配置(比如,你已经正确的设置了实体)。






Frequently Asked Questions

长见问题


Where is a managed object context created?

管理对象上下文在那里创建?

Where a managed object context comes from is entirely application-dependent. In a Cocoa document-based application using NSPersistentDocument, the persistent document typically creates the context, and gives you access to it through the managedObjectContext method.

管理对象上下文在那里创建是整个应用-决定的。在一个Cocoa 文档-基础 应用 使用NSPersistentDocument,这个持久文件通常创建这个上下文,和给你通过 managedObjectContext方法去访问它。


In a single-window application, if you create your project using the standard project assistant, the application delegate (the instance of the NSApplicationDelegate class) again creates the context, and gives you access to it through the managedObjectContext method. In this case, however, the code to create the context (and the rest of the Core Data stack) is explicit. It is written for you automatically as part of the template.

Do not use instances of subclasses of NSController directly to execute fetches. For example, do not create an instance of NSArrayController specifically to execute a fetch. Controllers are for managing the interaction between your model objects and your application interface. At the model object level, use a managed object context to perform the fetches directly.

在一个单-窗口应用中,如果你使用标准项目助理创建项目,这个应用代理(NSApplicationDelegate 类的实例)再次创建上下文,和让你去访问它通过managedObjectContext 方法。在这个场景中,代码去创建上下文(和CD栈的剩余)是明确的。它自动作为模版的一部分为你写入。不要用NSController 的子类的实例直接去执行获取。比如,不要创建一个NSArrayController的实例去执行一个获取。控制是为了管理你的模型对象和你的应用界面之间的交互。在模型对象层,使用管理对象去直接执行获取。



How do I initialize a store with default data?

如何使用默认数据去初始化一个存储?

There are two considerations here: creating the data, and ensuring the data is imported only once.

这里有两个需要考虑的:创建数据,和保证数据被导入仅仅一次。


There are several ways to create the data.

有很多方式去创建数据。


Create a separate persistent store that contains the default data and include the store as an application resource. When you want to use it, either copy the whole store to a suitable location, or copy the objects from the defaults store to an existing store. 

创建一个分开的包含默认数据的持久存储和包含着存储作为一个应用资源。当你想去使用它,要么复制整个存储去到合适位置,或拷贝对象从默认存储到一个存在的存储。


For small datasets, create the managed objects directly in code. 

Create a property list or another file-based representation of the data, and store it as an application resource. When you want to use it, open the file and parse the representation to create managed objects. 

对小数据集合,直接在代码中创建管理对象。创建一个属性列或另一个数据的文件-基础展示,和保存它作为一个应用资源。当你想去使用它,打开文件和解析那个展示去创建管理对象。


NOTE
Do not use this technique in iOS, and only if absolutely necessary in OS X. Parsing a file to create a store incurs unnecessary overhead. It is much better to create a Core Data store yourself offline and use it directly in your application. 

不要在iOS中使用这个技术,在OS X中必须要用的时候使用,解析一个文件去创建一个存储招致不必要的消耗。去创建一个CD更好和直接去创建它在你的应用中。

There are also several ways to ensure that the defaults are imported only once:

这里有很多方法去保证默认被导入仅仅一次:

If you are using iOS or creating an application for OS X that is not document-based, you can add a check on application launch to determine whether a file exists at the location you specify for the application’s store. If it doesn't, you need to import the data. 

If you are creating a document-based application using NSPersistentDocument, you initialize the defaults in initWithType:error:

如果你使用iOS或创建一个因为OS X不是文档-基础的,你能添加一个检查在应用启动中去决定是否一个文件存储于你明确给应用的存储的位置。如果它存在,你需要去导入数据。如果你创建一个文件-基础应用使用NSPersistentDocument,你在initWithType:error初始化这个默认值。


If there is a possibility that the store (hence file) might be created but the data not imported, then you can add a metadata flag to the store. You can check the metadata (using metadataForPersistentStoreWithURL:error: ) more efficiently than executing a fetch (and it does not require you to hard code any default data values).

如果有这种可能存储(因此文件)可能被创建但是数据没有导入,然后你添加一个元数据标签到这个存储。你能检查元数据(使用meradaForPersistentStoreWithURL:error:)更高效比执行一个获取(和它不需要你去硬编码任何模型数据值)。


How do I use my existing SQLite database with Core Data?

如何用CD去操作我的已经存在的SQLite 数据库?


You do not, unless you import your existing SQLite database into a Core Data store. Although Core Data supports SQLite as one of its persistent store types, the database format is private. You cannot create a SQLite database using the native SQLite API and use it directly with Core Data. If you have an existing SQLite database, you need to import it into a Core Data store. In addition, do not manipulate an existing Core Data-created SQLite store using the native SQLite API.

不可以,除非你导入你的存在的SQLite数据库到一个CD存储。尽管CD支持SQLite作为它的持久存储的一个类型。这个数据库的形式是私有的。你不能使用原生的SQLite 接口创建一个SQLite数据库和直接用CD操作它。如果你有一个存在SQLite 数据库,你需要去导入它到一个CD存储中。作为添加不要使用原生SQLite 接口 去 操作一个存储的CD-创建SQLite存储。

I have a to-many relationship from Entity A to Entity B. How do I fetch the instances of Entity B related to a given instance of Entity A?

我有一个对多关系从实体A到实体B。我如何获取联系到给定实体A的实例的实体B的实例?

You don’t. More specifically, there is no need to explicitly fetch the destination instances, you simply invoke the appropriate key-value coding or accessor method on the instance of Entity A. If the relationship is called widgets, then if you have implemented a custom class with a similarly named accessor method, you simply write:

不用。更明确来说,没有必要去明确获取目的实例,你只需调用和实现的键-值编码或访问方法在实体A的实例上。如果关系被叫做小部件,然后如果你已经实现了自定义的类使用相似命名的存取方法,你只需要写如下代码:

OBJECTIVE-C

NSSet *asWidgets = [instanceA widgets];

SWIFT

let asWidets = instanceA.widgets

Otherwise you use key-value coding:

OBJECTIVE-C

NSMutableSet *asWidgets = [instanceA mutableSetValueForKey:@"widgets"];

SWIFT

let asWidgets = instanceA.mutableSetValueForKey("widgets")



How do I fetch objects in the same order I created them?

我如何以我创建它们的顺序获取对象?

Objects in a persistent store are unordered. Typically you should impose order at the controller or view layer, based on an attribute such as creation date. If there is order inherent in your data, you need to explicitly model that.

在持久存储中的对象是无序的。通常你应该添加顺序在控制器或view层。基于一个属性比如创建日期。如果有固定的顺序在你的数据中,你需要去在模型中明确。




How do I copy a managed object from one context to another?

如何复制一个管理对象从一个上下文到另一个?

First, note that in a strict sense you are not copying the object. You are conceptually creating an additional reference to the same underlying data in the persistent store.

首先,注意在一场景你不赋值那个对象。你概念上创建一个额外引用到在持久存储中的相同的数据。

To copy a managed object from one context to another, you can use the object’s object ID, as illustrated in the following example.

去拷贝一个管理对象从一个上下文到另一个,你能使用管理对ID,就像在后面的代码中阐明的那样。


OBJECTIVE-C

NSManagedObjectID *objectID = [managedObject objectID];

NSManagedObject *copy = [context2 objectWithID:objectID];

SWIFT

let objectID = managedObject.objectID

let copy = context2.objectWithID(objectID)





I have a key whose value is dependent on values of attributes in a related entity—how do I ensure it is kept up to date as the attribute values are changed and as the relationship is manipulated?

我有一个键 它的值取决于一个有关的实体属性的值-如果去保证它是最新的随着属性值被改变和关系被操纵?







There are many situations in which the value of one property depends on that of one or more other attributes in another entity. If the value of one attribute changes, then the value of the derived property should also be flagged for change. How you ensure that key-value observing notifications are posted for these dependent properties depends on which version of OS X you’re using and the cardinality of the relationship.

有很多的场景在其中一个属性的值取决于一个或更多其他实体的属性。如果一个属性的值改变,然后这个被驱动属性的值也应该被标记改变。你如何确保键-值观察通知被传递为那些独立的属性取决于你在使用什么版本的OSX和关系的基数。


OS X v10.5 and later for a to-one relationship

OS X 10.5和以后一个对一关系


If there is a to-one relationship to the related entity, then to trigger notifications automatically you should either override keyPathsForValuesAffectingValueForKey: or implement a suitable method that follows the pattern keyPathsForValuesAffectingValueForKey: defines for registering dependent keys.

For example, you could override keyPathsForValuesAffectingValueForKey: as shown in the following example:

如果有一个对一关系到相关实体,然后去自动触发通知你应该重写keyPathsForValuesAffectingValueForKey:或实现一个合适的尊守了keyPathsForValuesAffectingValueForKey:模式定义的注册独立键的方法。

比如,你能从写keyPathsForValuesAffectingValueForKey:就像一下的代码中展示的这样。


OBJECTIVE-C

+ (NSSet *)keyPathsForValuesAffectingValueForKey:(NSString *)key {

    NSSet *keyPaths = [super keyPathsForValuesAffectingValueForKey:key];

    if ([key isEqualToString:@"fullNameAndDepartment"]) {

        NSSet *affectingKeys = [NSSet setWithObjects:@"lastName", @"firstName",

                                                    @"department.deptName", nil];

        keyPaths = [keyPaths setByAddingObjectsFromSet:affectingKeys];

    }

    return keyPaths;

}

SWIFT

class override func keyPathsForValuesAffectingValueForKey(key a href="" String /a ) ->  a href="" Set /a < a href="" String /a > {

    var keyPaths = super.keyPathsForValuesAffectingValueForKey(key)

    if key == "fullNameAndDepartment" {

        let affectingKeys a href="" Set /a  = ["lastName", "firstName", "department.deptName"]

        keyPaths = keyPaths.union(affectingKeys)

    }

    return keyPaths

}

Or, to achieve the same result, you could just implement keyPathsForValuesAffectingFullNameAndDepartment, as shown in the following example:

或去获得相同结果,你能实现keyPathsForValuesAffectingFullNameAndDepartment,就像下面代码展示的。

OBJECTIVE-C

+ (NSSet *)keyPathsForValuesAffectingFullNameAndDepartment {

 

    return [NSSet setWithObjects:@"lastName", @"firstName",

                                @"department.deptName", nil];

}

SWIFT

class func keyPathsForValuesAffectingFullNameAndDepartment() ->  a href="" Set /a < a href="" String /a > {

    return ["lastName", "firstName", "department.deptName"]

}



To-many relationships

对多关系

The keyPathsForValuesAffectingValueForKey: method does not allow keypaths that include a to-many relationship. For example, suppose you have a Department entity with a to-many relationship (employees) to an Employee, and Employee has a salary attribute. You might want the Department entity to have a totalSalary attribute that is dependent upon the salaries of all the Employees in the relationship. You can not do this with, for example, keyPathsForValuesAffectingTotalSalary and returning employees.salary as a key.

这个keyPathsForValuesAffectingValueForKey:方法不允许包含了对多关系的keypaths.比如,假如你有一个部门实体有一个对多关系(employees到一个雇员,和雇员有一个salary属性。你可能想部门实体去有一个取决于所有在这个关系的雇员的薪水的totalSalary属性。你不能这样做,比如,keyPathsForValuesAffectingTotalSalary和返回employees.salary做为一个键。


There are two possible solutions:

这里有两种可能解决

Use key-value observing to register the parent (in this example, Department) as an observer of the relevant attribute of all the children (Employees in this example). You must add and remove the parent as an observer as child objects are added to and removed from the relationship (see Registering for Key-Value Observing). In the observeValueForKeyPath:ofObject:change:context: method you update the dependent value in response to changes, as illustrated in the following code fragment: 

使用键-值坚持去注册父类(在这个例子中,部门)作为一个所有子体(雇员在这个例子)相关属性的观测者。你必须添加或一场父类作为观测者随着子类被添加和移除从关系中(参见Registering for Key-Value Observing).在这个obserValueForKeyPath:ofObject:change:context:方法你更新到相应改变的独立值,就像代码中阐明的这样:




OBJECTIVE-C

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {

 

    if (context != totalSalaryContext) {

        return [super observeValueForKeyPath:keyPath ofObject:object change:change context:context];

    }

    if ([keyPath isEqualToString:@"totalSalary"]) {

        [self setTotalSalary:[self valueForKeyPath:@"employees.@sum.salary"]];

    }

    // Deal with other observations and/or invoke super...

}


SWIFT

override func observeValueForKeyPath(keyPath a href="" String /a ?, ofObject object a href="" AnyObject /a ?, change: [ a href="" NSObject /a  a href="" AnyObject /a ]?, context a href="" UnsafeMutablePointer /a < a href="" Void /a >) {

    guard let keyPath = keyPath else {

        super.observeValueForKeyPath(keyPath, ofObject: object, change: change, context: context)

        return

    }

    switch (keyPath, context) {

    case("totalSalary", &totalSalaryContext):

        totalSalary = valueForKeyPath("employees.@sum.salary") as a href="" NSNumber /a 

    default:

        print("Unexpected key: \(keyPath)")

    }

}



You can register the parent with the application's notification center as an observer of its managed object context. The parent should respond to relevant change notifications posted by the children in a manner similar to that for key-value observing.

你能使用应用的通知中心注册做为它的管理对象上下文的观察者。这个父类应该响应到相关被子类发送的改变通知 以一个和key-value观察相同的方式。


In Xcode’s predicate builder, why don’t I see any properties for a fetched property predicate?

在Xcode的谓词建设器,为什么没有人和谓词为一个获取属性谓词?

If you want to create a predicate for a fetched property in the predicate builder in Xcode, but don’t see any properties, you probably have not set the destination entity for the fetched property.

如果你想去创建一个谓词为一个获取属性在一个谓词建造在Xcode,但是没有看到任何属性,你可能还没有设置目的实体为一个获取属性。







How efficient is Core Data?

CD有多么高效

Throughout the development of Core Data, the engineering team compared the runtime performance of a generic Core Data application with that of a similar application developed without using Core Data. In general, the Core Data implementation performed better. There may nevertheless be opportunities for further optimization, and the team continues to pursue performance aggressively. For a discussion of how you can ensure you use Core Data as efficiently as possible, see Performance.

CD的开发的各个地方,引擎团队对比一个通用的CD应用的运行时执行和一个相同的应用没有使用CD开发。通常来说实现CD执行更好,团队继续去优化之行。对于你能如何确保你能最高效的使用CD,参见Performance.


OS X

These questions are only relevant to using Core Data with OS X.

那些问题仅仅关系到在OSX中使用CD。


How do I get the GUI to validate the data entered by the user?

Core Data validates all managed objects when a managed object context is sent a save: message. In a Core Data document-based application, this message is sent when the user saves the document. To have the GUI validate the data as it is being entered, select the Validates Immediately option for a value binding in the Interface Builder Bindings inspector. If you establish the binding programmatically, you supply in the binding options dictionary a value of YES (as an NSNumber object) for the key a href="" NSValidatesImmediatelyBindingOption /a . See a href="" target="_self" Binding Options /a .

For details of how to write custom validation methods, see the subclassing notes for NSManagedObject.


When I remove objects from a detail table view managed by an array controller, why are they not removed from the object graph?

If an array controller manages the collection of objects at the destination of a relationship, then by default the remove method simply removes the current selection from the relationship. If you want removed objects to be deleted from the object graph, then you need to enable the Deletes Objects On Remove option for the contentSet binding in Interface Builder.


How do I get undo/redo for free in my nondocument app?

In a Core Data document-based application, the standard NSDocument undo manager is replaced by the undo manager of the document’s managed object context. In a nondocument application for OS X, your window’s delegate can supply the managed object context’s undo manager using the a href="" windowWillReturnUndoManager: /a delegate method. If your window delegate has an accessor method for the managed object context (as is the case if you use the Core Data Application template), your implementation of a href="" windowWillReturnUndoManager: /a might be as follows:

OBJECTIVE-C

- (NSUndoManager *)windowWillReturnUndoManager:(NSWindow *)sender {

    return [[self managedObjectContext] undoManager];

}

SWIFT

func windowWillReturnUndoManager(window a href="" NSWindow /a ) ->  a href="" NSUndoManager /a ? {

    return managedObjectContext!.undoManager

}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值