【IOS开发高级系列】CoreData专题

作者: Kevin_Junbaozi | 来源:发表于2018-04-11 22:32 被阅读109次

    1 CoreData运行机制

    1.1 CoreData总体架构

    1.1.1 CoreData架构图

    CoreData架构图:

    1.1.2 主要术语

    1,Managed Object Model

            Managed Object Model 是描述应用程序的数据模型,这个模型包含实体(Entity),特性(Property),读取请求(Fetch Request)等。(下文都使用英文术语。)

    2,Managed Object Context

            Managed Object Context 参与对数据对象进行各种操作的全过程,并监测数据对象的变化,以提供对 undo/redo 的支持及更新绑定到数据的 UI。

    3,Persistent Store Coordinator

            Persistent Store Coordinator 相当于数据文件管理器,处理底层的对数据文件的读取与写入。一般我们无需与它打交道。

    4,Managed Object

            Managed Object 数据对象,与 Managed Object Context 相关联。

    1.1.3 初始化机制

            1,应用程序先创建或读取模型文件(后缀为xcdatamodeld)生成 NSManagedObjectModel 对象。Document应用程序是一般是通过 NSDocument 或其子类 NSPersistentDocument)从模型文件(后缀为 xcdatamodeld)读取。

            2,然后生成 NSManagedObjectContext 和 NSPersistentStoreCoordinator 对象,前者对用户透明地调用后者对数据文件进行读写。

            3,NSPersistentStoreCoordinator 负责从数据文件(xml, sqlite,二进制文件等)中读取数据生成 Managed Object,或保存 Managed Object 写入数据文件。

            4,NSManagedObjectContext 参与对数据进行各种操作的整个过程,它持有 Managed Object。我们通过它来监测 Managed Object。监测数据对象有两个作用:支持 undo/redo 以及数据绑定。这个类是最常被用到的。

            5,Array Controller, Object Controller, Tree Controller 这些控制器一般与 NSManagedObjectContext 关联,因此我们可以通过它们在 nib 中可视化地操作数据对象。

    1.2 Model Classes

            模型有点像数据库的表结构,里面包含 Entry, 实体又包含三种 Property:Attribute(属性),RelationShip(关系), Fetched Property(读取属性)。Model class 的名字多以 "Description" 结尾。我们可以看出:模型就是描述数据类型以及其关系的。

            主要的 Model class 有:

        1)Entity - NSEntityDescription

            Entity 相当于数据库中的一个表,它描述一种抽象数据类型,其对应的类为 NSManagedObject 或其子类。

            NSEntityDescription 常用方法:

    +insertNewObjectForEntityForName:inManagedObjectContext:工厂方法,根据给定的 Entity 描述,生成相应的 NSManagedObject 对象,并插入 ManagedObjectContext 中。

    -managedObjectClassName 返回映射到 Entity 的 NSManagedObject 类名

    -attributesByName 以名字为 key, 返回 Entity 中对应的Attributes

    -relationshipsByName 以名字为 key, 返回 Entity 中对应的Relationships

        2)Property - NSPropertyDescription

            Property 为 Entity 的特性,它相当于数据库表中的一列,或者 XML 文件中的 value-key 对中的 key。它可以描述实体数据(Attribute),Entity之间的关系(RelationShip),或查询属性(Fetched Property)。

        > Attribute - NSAttributeDescription

            Attribute 存储基本数据,如 NSString,

            NSNumber or NSDate 等。它可以有默认值,也可以使用正则表达式或其他条件对其值进行限定。一个属性可以是 optional 的。

        > Relationship -NSRelationshipDescription

            Relationship 描述 Entity,Property 之间的关系,可以是一对一,也可以是一对多的关系。

        > Fetched Property - NSFetchedPropertyDescription

            Fetched Property 根据查询谓词返回指定 Entity 的符合条件的数据对象。

    1.3 CoreData操作对象

    1.3.1 NSManagedObject

    > Managed Object - NSManagedObject

            Managed Object 表示数据文件中的一条记录,每一个 Managed Object 在内存中对应 Entity 的一个数据表示。Managed Object 的成员为 Entity 的 Property 所描述。

            每一个 Managed Object 都有一个全局 ID(类型为:NSManagedObjectID)。Managed Object 会附加到一个 Managed Object Context,我们可以通过这个全局 ID 在 Managed Object Context 查询对应的 Managed Object。

    NSManagedObject 常用方法

    -entity获取其Entity

    -objectID获取其Managed Object  ID

    -valueForKey:获取指定 Property 的值

    -setValue: forKey:设定指定 Property 的值

    1.3.2 NSManagedObjectContext

    > Managed Object Context -NSManagedObjectContext

            Managed Object Context 的作用相当重要,对数据对象进行的操作都与它有关。当创建一个数据对象并插入 Managed Object Context 中,Managed Object Context 就开始跟踪这个数据对象的一切变动,并在合适的时候提供对 undo/redo 的支持,或调用 Persistent Store Coordinato 将变化保存到数据文件中去。

            通常我们将 controller 类(如:NSArrayController,NSTreeController)或其子类与 Managed Object Context 绑定,这样就方便我们动态地生成,获取数据对象等。

    NSManagedObjectContext 常用方法

    -save:将数据对象保存到数据文件

    -objectWithID:查询指定 Managed Object ID 的数据对象

    -deleteObject:将一个数据对象标记为删除,但是要等到 Context 提交更改时才真正删除数据对象

    -undo回滚最后一步操作,这是都 undo/redo 的支持

    -lock加锁,常用于多线程以及创建事务。同类接口还有:-unlock and -tryLock

    -rollback还原数据文件内容

    -reset清除缓存的 Managed Objects。只应当在添加或删除 Persistent Stores 时使用

    -undoManager返回当前 Context 所使用的NSUndoManager

    -assignObject: toPersistantStore:由于 Context 可以管理从不同数据文件而来的数据对象,这个接口的作用就是指定数据对象的存储数据文件(通过指定 PersistantStore 实现)

    -executeFetchRequest: error:执行 Fetch Request 并返回所有匹配的数据对象

    1.3.3 NSPersistentStoreCoordinator

    > Persistent Store Coordinator -NSPersistentStoreCoordinator

            使用 Core Data document 类型的应用程序,通常会从磁盘上的数据文中中读取或存储数据,这写底层的读写就由 Persistent Store Coordinator 来处理。一般我们无需与它直接打交道来读写文件,Managed Object Context 在背后已经为我们调用 Persistent Store Coordinator 做了这部分工作。

    NSPersistentStoreCoordinator 常用方法

    -addPersistentStoreForURL:configuration:URL:options:error: 装载数据存储,对应的卸载数据存储的接口为-removePersistentStore:error:

    -migratePersistentStore:toURL:options:withType:error: 迁移数据存储,效果与 "save as"相似,但是操作成功后,迁移前的数据存储不可再使用

    -managedObjectIDForURIRepresentation: 返回给定 URL所指示的数据存储的 object id,如果找不到匹配的数据存储则返回nil

    -persistentStoreForURL: 返回指定路径的Persistent  Store

    -URLForPersistentStore: 返回指定 Persistent Store 的存储路径

    1.3.4 NSPersistentDocument

    > Persistent Document -NSPersistentDocument

            NSPersistentDocument是 NSDocument 的子类。 multi-document Core Data 应用程序使用它来简化对 Core Data 的操作。通常使用NSPersistentDocument 的默认实现就足够了,它从 Info.plist 中读取 Document types 信息来决定数据的存储格式(xml,sqlite, binary)。

    NSPersistentDocument 常用方法

    -managedObjectContext返回文档的 Managed Object Context,在多文档应用程序中,每个文档都有自己的 Context。

    -managedObjectModel返回文档的Managed Object  Model

    1.4 查询Fetch Requests

            Fetch Requests 相当于一个查询语句,你必须指定要查询的 Entity。我们通过 Fetch Requests 向 Managed Object Context 查询符合条件的数据对象,以 NSArray 形式返回查询结果,如果我们没有设置任何查询条件,则返回该 Entity 的所有数据对象。我们可以使用谓词来设置查询条件,通常会将常用的 Fetch Requests 保存到 dictionary 以重复利用。

    示例:

    NSManagedObjectContext * context  = [[NSApp delegate] managedObjectContext];

    NSManagedObjectModel * model    = [[NSApp delegate] managedObjectModel];

    NSDictionary * entities = [model entitiesByName];

    NSEntityDescription *entity   = [entities valueForKey:@"Post"];

    NSPredicate * predicate;

    predicate = [NSPredicate predicateWithFormat:@"creationDate > %@", date];

    NSSortDescriptor * sort = [[NSortDescriptor alloc] initWithKey:@"title"];

    NSArray * sortDescriptors = [NSArray arrayWithObject: sort];

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

    [fetch setEntity: entity];

    [fetch setPredicate: predicate];

    [fetch setSortDescriptors: sortDescriptors];

    NSArray * results = [context executeFetchRequest:fetch error:nil];

    [sort release];

    [fetch release];

            在上面代码中,我们查询在指定日期之后创建的 post,并将查询结果按照 title 排序返回。

    NSFetchRequest 常用方法

    -setEntity:设置你要查询的数据对象的类型(Entity)

    -setPredicate:设置查询条件

    -setFetchLimit:设置最大查询对象数目

    -setSortDescriptors:设置查询结果的排序方法

    -setAffectedStores:设置可以在哪些数据存储中查询

    2 CoreData PG概述

        Using the Core Data framework, most of this functionality is provided for you automatically, primarily through an object known as a managed object context (or just “context”). The managed object context serves as your gateway to an underlying collection of framework objects—collectively known as the persistence stack—that mediate between the objects in your application and external data stores. At the bottom of the stack are persistent object stores, as illustrated in Figure 2 (page 19).

    2.1 Managed Objects and Contexts

            You can think of a 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.

            Model objects that tie into in the Core Data framework are known as managed objects. All managed objects must be registered with a managed object context. You add objects to the graph and remove objects from the graph using the context. 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.

            You may have more than one managed object context in your application. For every object in a persistent store there may be at most one corresponding managed object associated with a given context(for more details, see Faulting and Uniquing (page 108)). To consider this from a different perspective, a given object in a persistent store may be edited in more than one context simultaneously. Each context, however, has its own managed object that corresponds to the source object, and each managed object may be edited independently. This can lead to inconsistencies during a save—Core Data provides a number of ways to deal with this (see, for example, Using Managed Objects (page 67)).

    2.2 Fetch Requests

            To retrieve data using a managed object context, you create a fetch request. A fetch request is an object that specifies what data you want, for example, “all Employees,” or “all Employees in the Marketing department ordered by salary, highest to lowest.” A fetch request has three parts. Minimally it must specify the name of an entity(by implication, you can only fetch one type of entity at a time). It may also contain a predicate object that specifies conditions that objects must match and an array of sort descriptor objects that specifies the order in which the objects should appear, as illustrated inFigure 3 (page 21).

            You send a fetch request to a managed object context, which returns the objects that match your request(possibly none) from the data sources associated with its persistent stores. Since all managed objects must be registered with a managed object context, objects returned from a fetch are automatically registered with the context you used for fetching. Recall though that for every object in a persistent store there may be at most one corresponding managed object associated with a given context (see Faulting and Uniquing (page 108)).If a context already contains a managed object for an object returned from a fetch, then the existing managed object is returned in the fetch results.

    2.3 Persistent Store Coordinator

            In effect, a persistent store coordinator defines a stack. The coordinator is designed to present a façade to the managed object contexts so that a group of persistent stores appears as a single aggregate store. A managed object context can then create an object graph based on the union of all the data stores the coordinator covers. A coordinator can only be associated with one managed object model. If you want to put different entities into different stores, you must partition your model entities by defining configurations within the managed object models (see Configurations (page 29)). Figure 4 (page 22) shows an example where employees and departments are stored in one file, and customers and companies in another. When you fetch objects, they are automatically retrieved from the appropriate file, and when you save, they are archived to the appropriate file.

    2.4 Persistent Stores

            A given persistent object store is associated with as ingle file or other external data store and is ultimately responsible for mapping between data in that store and corresponding objects in a managed object context. Normally, the only interaction you have with a persistent object store is when you specify the location of a new external data store to be associated with your application (for example, when the user opens or saves a document). Most other interactions with the Core Data framework are through the managed object context.

            Your application code—and in particular the application logic associated with managed objects—should not make any assumptions about the persistent store in which data may reside. Core Data provides native support for several file formats. You can choose which to use depending on the needs of your application. If at some stage you decide to choose a different file format, your application architecture remains unchanged. Moreover, if your application is suitably abstracted, then you will be able to take advantage of later enhancements to the framework without any additional effort. For example—even if the initial implementation is able to fetch records only from the local file system—if an application makes no assumptions about where it gets its data from, then if at some later stage support is added for a new typeof remote persistent store, it should be able to use this new type with no code revisions.

            Important: Although Core Data supports SQLite as one of its persistent store types, Core Data cannot manage any arbitrary SQLite database. In order to use a SQLite database, Core Data must create and manage the database itself. For more about store types, see Persistent Store Features (page 128).

    2.5 Persistent Documents

            You can create and configure the persistence stack programmatically. In many cases, however, you simply want to create a document-based application able to read and write files. The NSPersistentDocument class is a subclass of NSDocument that is designed to let you easily take advantage of the Core Data framework. By default, an NSPersistentDocument instance creates its own ready-to-use persistence stack, including a managed object context and a single persistent object store. There is in this case a one-to-one mapping between a document and an external data store. The NSPersistentDocument class provides methods to access the document’s managed object context and provides implementations of the standard NSDocument methods to read and write files that use the Core Data framework. By default you do not have to write any additional code to handle object persistence. A persistent document’s undo functionality is integrated with the managed object context.

    2.6 Managed Objects and the Managed Object Model

            In order both to manage the object graph and to support object persistence, Core Data needs a rich description of the objects it operates on. A managed object model is a schema that provides a description of the managed objects, or entities, used by your application, as illustrated in Figure 5 (page 24). You typically create the managed object model graphically using Xcode's Data Model Design tool. (If you wish you can construct the model programmatically at runtime.)

            The model is composed of a collection of entity description objects that each provide metadata about an entity, including the entity's name, the name of the class that represents it in your application (this does not have to be the same as its name), and its attributes and relationships. The attributes and relationships in turn are represented by attribute and relationship description objects, as illustrated in Figure 6 (page 24).

            Managed objects must be instances of either NSManagedObject or of a subclass of NSManagedObject. NSManagedObject is able to represent any entity. It uses a private internal store to maintain its properties and implements all the basic behavior required of a managed object. A managed object has a reference to the entity description for the entity of which it is an instance. It refers to the entity description to discover metadata about itself, including the name of the entity it represents and information about its attributes and relationships. You can also create subclasses of NSManagedObject to implement additional behavior.

    3 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. The schema is represented by a managed object model—an instance of NSManagedObjectModel. In general, the richer the model, the better Core Data is able to support your application.

    3.1 Features of a Managed Object Model

            A managed object model is an instance of the NSManagedObjectModel class. It describes a schema—a collection of entities—that you use in your application.

    3.1.1 Entities

            A model contains NSEntityDescription objects that represent the model's entities. Two important features of an entity are its name, and the name of the class used to represent the entity at runtime. You should be careful to keep clear the differences between an entity, the class used to represent the entity, and the managed objects that are instances of that entity. An NSEntityDescription object may have NSAttributeDescription and NSRelationshipDescription objects that represent the properties of the entity in the schema. An entity may also have fetched properties, represented by instances of NSFetchedPropertyDescription, and the model may have fetch request templates, represented by instances of NSFetchRequest. In a model, entities may be arranged in an inheritance hierarchy, and entities may be specified as abstract.

    3.1.2 EntityInheritance

            Entity inheritance works in a similar way to class inheritance, and is useful for the same reasons. If you create a model using the data modeling tool in Xcode, you specify an entity's parent by selecting the name of the entity from the Parent pop-up menu in the entity Info pane, as shown in Figure 1 (page 27).

            If you want to create an entity inheritance hierarchy in code, you must build it top-down. You cannot set an entity’s super-entity directly, you can only set an entity’s sub-entities (using the method set Subentities). To set a super-entity for a given entity, you must therefore set an array of sub-entities for that super entity and include the current entity in that array.

    3.1.3 AbstractEntities

            You can specify that an entity is abstract—that is, that 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 which should not itself be instantiated. For example, in a drawing application you might have a Graphic entity that defines attributes for x and y coordinates, color, and drawing bounds. You never, though, instantiate a Graphic. Concrete sub-entities of Graphic might be Circle,TextArea, and Line.

    3.1.4 Properties

            An entity's properties are its attributes and relationships, including its fetched properties (if it has any). Amongst other features, each property has a name and a type. Attributes may also have a default value. 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 properties are properties that you define as part of the model, but which are not saved to the persistent store as part ofan entity instance's data. Core Data does track changes you make to transient properties, so they are recorded for undo operations.

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

    3.1.5 Attributes

            Core Data natively supports a variety of attribute types, such as string, date, and integer (represented as instances ofNSString, NSDate, and NSNumber respectively). If you want to use an attribute type that is not natively supported, you can use one of the techniques described in Non-Standard Persistent Attributes (page89).

    避免允许空值,使用默认值代替

            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—in the model—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.

    false ==(NULL == 0)

    false == (NULL != 0)

            Moreover, NULL in a database is not equivalent to an empty string or empty data blob, either:

    false ==(NULL == @"")

    false == (NULL != @"")

    3.1.6 Relationships

            Core Data supports to-one and to-many relationships, and fetched properties. Fetched properties represent weak, one-way relationships. You can specify the optionality and cardinality of a relationship, and its delete rule. You should typically model a relationship in both directions. A many-to-many relationship is one in which a relationship and its inverse are both to-many. Relationships are described in greater detail in Relationships and Fetched Properties (page80).

    3.1.7 FetchRequest Templates

    预定义查询语句

            You can predefine fetch requests and store them in a managed object model as named templates. This allows you to pre-define queries that you can retrieve as necessary from the model. Typically, you define fetch request templates using the Xcode data modeling tool (see Xcode Tools for CoreData ). The template may include variables, as shown in Figure 2.    

        For more about using fetch request templates, see Accessing and Using a Managed Object Model at Runtime (page 33).

    3.1.8 User Info Dictionaries

            Many of the elements in a managed object model—entities, attributes, and relationships—have an associated user info dictionary. You can put whatever information you want into a user info dictionary, as key-value pairs. Common information to put into the user info dictionary includes version details for an entity, and values used by the predicate for a fetched property.

    3.1.9 Configurations(可能可以支持多线程)

            A configuration has a name and an associated set of entities. The sets may overlap—that is, a given entity may appear in more than one configuration. You establish configurations programmatically using setEntities:forConfiguration: or using the Xcode data modeling tool (see Xcode Tools for Core Data ), and retrieve the entities for a given configuration name using entitiesForConfiguration:. 

            You typically use configurations if you want to store different entities in different stores. A persistent store coordinator can only have one managed object model, so by default each store associated with a given coordinator must contain the same entities. To work around this restriction, you can create a model that contains the union of all the entities you want to use. You then create configurations in the model for each of the subsets of entities that you want to use. You can then use this model when you create a coordinator. When you add stores, you specify the different store attributes by configuration. When you are creating your configurations, though, remember that you cannot create cross-store relationships.

    NSManagedObjectModel类中方法

    - (void)setEntities:(NSArray *)entities   forConfiguration:(NSString *) configuration

    3.2 Using a Managed Object Model

    3.2.1 Creating and Loading a Managed Object Model

            You usually create a model in Xcode, as described in Core Data Model Editor Help . You can also create a model entirely in code, as show in Listing 3 (page 37) and described in Core Data UtilityTutorial —typically, however, this is too long-winded to consider in anything but the most trivial application. (You are nevertheless encouraged to review the tutorial to gain an understanding of what the modeling tool does, and in particular to gain an appreciation that the model is simply a collection of objects.)

    3.2.2 Compiling a Data Model

            A data model is a deployment resource. In addition to details of the entities and properties in the model, a model you create in Xcode contains information about the diagram—its layout, colors of elements, and so on. This latter information is not needed at runtime. The model file is compiled using the model compiler, momc, to remove the extraneous information and make runtime loading of the resource as efficient as possible. An xcdatamodeld “source” directory is compiled into a momd deployment directory, and an xcdatamodel “source” file is compiled into a mom deployment file. momc is located in /Developer/usr/bin/. If you want to use it in your own build scripts, its usage is momc source destination, where source is the path of the Core Data model to compile and destination is the path of the output.

    3.3 开发技巧

    3.3.1 modelURL的文件名必须与创建的CoreDataDataModel文件名相同

    - (NSManagedObjectModel*)managedObjectModel {

        // 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.

        if (_managedObjectModel != nil) {

            return _managedObjectModel;

        }

        //此处文件名必须与创建的CoreDataDataModel文件(即后缀为xcdatamodeld的文件)的文件名相同

        NSURL *modelURL = [[NSBundle mainBundle] URLForResource: @"HJDevDataModel" withExtension: @"mom"];

        if (modelURL == nil)

        {

            // The model may be versioned or created with Xcode 4, try momd as an extension.

            modelURL =[[NSBundle mainBundle] URLForResource: @"HJDevDataModel" withExtension: @"momd"];

        }

        if(modelURL)

        {

            // If path is nil, then NSURL or NSManagedObjectModel will throw an exception

            _managedObjectModel = [[[NSManagedObjectModel alloc] initWithContentsOfURL: modelURL] copy];

        }

        return _managedObjectModel;

    }

    4 persistentStoreCoordinator

    5 ManageObjectContext

    5.1 数据变化通知

    // Notifications immediately before and immediately after the contextsaves.  The user info dictionary containsinformation about the objects that changed and what changed

    COREDATA_EXTERN NSString * constNSManagedObjectContextWillSaveNotification NS_AVAILABLE(10_5, 3_0);

    COREDATA_EXTERN NSString * constNSManagedObjectContextDidSaveNotification NS_AVAILABLE(10_4, 3_0);

    // Notification when objects in a context changed:  the user info dictionary contains informationabout the objects that changed and what changed

    COREDATA_EXTERN NSString * constNSManagedObjectContextObjectsDidChangeNotification NS_AVAILABLE(10_4, 3_0);   

    // User info keys forNSManagedObjectContextObjectsDidChangeNotification:  the values for these keys are sets of managedobjects

    COREDATA_EXTERN NSString * constNSInsertedObjectsKey NS_AVAILABLE(10_4, 3_0);

    COREDATA_EXTERN NSString * constNSUpdatedObjectsKey NS_AVAILABLE(10_4, 3_0);

    COREDATA_EXTERN NSString * constNSDeletedObjectsKey NS_AVAILABLE(10_4, 3_0);

    COREDATA_EXTERN NSString * constNSRefreshedObjectsKey NS_AVAILABLE(10_5, 3_0);

    COREDATA_EXTERN NSString * constNSInvalidatedObjectsKey NS_AVAILABLE(10_5, 3_0);

    // User info keys forNSManagedObjectContextObjectsDidChangeNotification:  the values for these keys are arrays ofobjectIDs

    COREDATA_EXTERN NSString * constNSInvalidatedAllObjectsKey NS_AVAILABLE(10_5, 3_0); 

    // All objects in the context have been invalidated

    5.2 多Context共享StoreCoordinator的CoreData架构

    5.2.1 方案设计思想

    (Good)Multi-ContextCoreData

    http://www.cocoanetics.com/2012/07/multi-context-coredata/

    此方案采用典型的三层架构:

    父Context为后台写队列Context;

    子Context为MainThread页面同步Context;

    孙Context为数据读取的子线程Context;

    5.2.2 代码示例

    5.2.2.1 Context初始化

    -(NSManagedObjectContext*) getRootManageObjectContext

    {

        if (_rootManagedObjectContext) {

            return _rootManagedObjectContext;

        }

        NSPersistentStoreCoordinator * coordinator = [self persistentStoreCoordinator];

        if(coordinator)

        {

            if ([NSManagedObjectContext instancesRespondToSelector: @selector(initWithConcurrencyType:)]){

                _rootManagedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType: NSPrivateQueueConcurrencyType];

            }

            else

            {

                _rootManagedObjectContext = [[NSManagedObjectContext alloc] init];

            }

            //内存数据优先

            _rootManagedObjectContext.mergePolicy = NSMergeByPropertyObjectTrumpMergePolicy;

            _rootManagedObjectContext.persistentStoreCoordinator= coordinator;

            _rootManagedObjectContext.undoManager = nil;

        }

        return _rootManagedObjectContext;

    }

    -(NSManagedObjectContext*)getMainThreadManagedObjectContext

    {

        if (_mainThreadManagedObjectContext) {

            return _mainThreadManagedObjectContext;

        }

        NSPersistentStoreCoordinator * coordinator = [self persistentStoreCoordinator];

        if(coordinator)

        {

            if ([NSManagedObjectContext instancesRespondToSelector: @selector(initWithConcurrencyType:)]){

                _mainThreadManagedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType: NSMainQueueConcurrencyType];

            }

            else

            {

                _mainThreadManagedObjectContext = [[NSManagedObjectContext alloc] init];

            }

            _mainThreadManagedObjectContext.undoManager = nil;

            _mainThreadManagedObjectContext.parentContext = [self getRootManageObjectContext];

        }

        return _mainThreadManagedObjectContext;

    }

    - (NSManagedObjectContext*) getQueryManageObjectContext

    {

        if (_queryManagedObjectContext) {

            return _queryManagedObjectContext;

        }

        NSPersistentStoreCoordinator * coordinator = [self persistentStoreCoordinator];

        if(coordinator)

        {

            if ([NSManagedObjectContext instancesRespondToSelector: @selector(initWithConcurrencyType:)]){

                _queryManagedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType: NSPrivateQueueConcurrencyType];

            }

            else

            {

                _queryManagedObjectContext = [[NSManagedObjectContext alloc] init];

            }

            _queryManagedObjectContext.undoManager = nil;

            _queryManagedObjectContext.parentContext = [self getMainThreadManagedObjectContext];

            //内存数据优先

            _queryManagedObjectContext.mergePolicy = NSMergeByPropertyObjectTrumpMergePolicy;

        }

        return _queryManagedObjectContext;

    }

    5.2.2.2 ChangesMerge监听

    - (void) commonInit

    {

        _pageMax = [[HJDataPage alloc] init];

        _pageMax.pageIndex = 0;

        _pageMax.size = 1000;// INT_MAX;// 不能用NSIntegerMax,其在6以上是64bit数据,会引起索引值越界;

        _mdCoreDataLargeQueryQueue = dispatch_queue_create("com.hj.dev.database", DISPATCH_QUEUE_CONCURRENT);

        [self addNotifications];

    }

    -(void) addNotifications

    {

        [[NSNotificationCenter defaultCenter] addObserverForName:NSManagedObjectContextDidSaveNotification object:nil queue:nil usingBlock:^(NSNotification*note){

            NSManagedObjectContext *savedContext = note.object;

            NSManagedObjectContext *moc = [self getMainThreadManagedObjectContext];

            if (moc.persistentStoreCoordinator != savedContext.persistentStoreCoordinator)

            {

                // that's another database

                return;

            }

            if (savedContext != moc)

            {

                [mocperformBlock:^(){

                    [moc mergeChangesFromContextDidSaveNotification:note];

                }];

            }

        }];

    }

    5.3 一个Context对应一个StoreCoordinator的CoreData架构

    5.3.1 方案设计思路

            对于多线程环境下得CoreData操作,一个Context对应一个StoreCoordinator的方案最安全,但是内存消耗也相应更多,因此除非必要,不要轻易使用此方案。

    5.3.2 示例代码

    5.3.2.1 Context初始化

    - (NSManagedObjectContext*)getLargeWriteManageObjectContext

    {

        NSManagedObjectContext *context;

        NSPersistentStoreCoordinator *coordinator = [self newPersistentStoreCoordinator];

        if(coordinator)

        {

            if ([NSManagedObjectContext instancesRespondToSelector: @selector(initWithConcurrencyType:)]){

                context = [[NSManagedObjectContext alloc] initWithConcurrencyType: NSPrivateQueueConcurrencyType];

            }

            else

            {

                context = [[NSManagedObjectContext alloc] init];

            }

            context.undoManager = nil;

            context.persistentStoreCoordinator= coordinator;

            //内存数据优先

            context.mergePolicy = NSMergeByPropertyObjectTrumpMergePolicy;

        }

        returncontext;

    }

    - (NSPersistentStoreCoordinator*) newPersistentStoreCoordinator

    {

        // Create the coordinator and store

        NSPersistentStoreCoordinator *persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel: self managedObjectModel]];

        NSURL *storeURL = [[self applicationDocumentsDirectory] URLByAppendingPathComponent: @"HJDevCoreData.sqlite"];

        NSError *error = nil;

        NSString *failureReason = @"There was an error creating or loading the application's saved data.";

        if (![persistentStoreCoordinator addPersistentStoreWithType: NSSQLiteStoreType configuration: nil URL: storeURL options: _storeOptions error: &error]) {

            // Report any error we got.

            NSMutableDictionary *dict = [NSMutableDictionary dictionary];

            dict[NSLocalizedDescriptionKey] = @"Failed to initialize the application's saved data";

            dict[NSLocalizedFailureReasonErrorKey] = failureReason;

            dict[NSUnderlyingErrorKey] = error;

            error = [NSError errorWithDomain: @"YOUR_ERROR_DOMAIN" code: 9999 userInfo: dict];

            // Replace this with code to handle the error appropriately.

            // abort() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.

            NSLog(@"Unresolved error %@, %@", error, [error userInfo]);

            abort();

        }

        return persistentStoreCoordinator;

    }

    5.3.2.2 ChangesMerge监听

    - (void) commonInit

    {

        _pageMax = [[HJDataPage alloc] init];

        _pageMax.pageIndex = 0;

        _pageMax.size = 10000;// INT_MAX;// 不能用NSIntegerMax,其在6以上是64bit数据,会引起索引值越界;

        _hjCoreDataQueryQueue = dispatch_queue_create("com.hj.dev.database", DISPATCH_QUEUE_CONCURRENT);

        [self addNotifications];

    }

    -(void) addNotifications

    {

        [[NSNotificationCenter defaultCenter] addObserverForName: NSManagedObjectContextDidSaveNotification object: nil queue: nil usingBlock: ^(NSNotification*note){

            NSManagedObjectContext *savedContext = note.object;

            NSManagedObjectContext *moc = [self getMainThreadManagedObjectContext];

            if (moc.persistentStoreCoordinator != savedContext.persistentStoreCoordinator)

            {

                // that's another StoreCoordinator

                return;

            }

            if(savedContext != moc)

            {

                [moc performBlock:^(){

                    [moc mergeChangesFromContextDidSaveNotification: note];

                }];

            }

        }];

    }

    6 NSManagedObject

    7 数据库操作

    7.1 查询

    7.1.1 NSPredicate

            NSPredicate用于查询和过滤在SQL中作为查询条件通常用WHERE,但在CORE DATA中作为查询条件就可以用到NSPredicate. NSPredicate 不单可以和CORE DATA中的FetchRequest 配合使用。也可以与NSArray配合使用。

    7.1.1.1 NSPredicate中支持的关键词和条件符

        1、>,<,>=,<=,= 比较运算符。如:

     NSPredicate * qcondition= [NSPredicate predicateWithFormat: @"salary >= 10000"];

        2、字符串操作(包含):BEGINSWITH、ENDSWITH、CONTAINS,如:

         @"employee.name BEGINSWITH[cd] '李'" //姓李的员工

         @"employee.name ENDSWITH[c] '梦'" //以梦结束的员工

         @"employee.name CONTAINS[d] '宗'" //包含有"宗"字的员工

            注:[c]不区分大小写[d]不区分发音符号即没有重音符号[cd]既不区分大小写,也不区分发音符号。

    3、范围:IN ,BWTEEN,如:

         @"salary BWTEEN{5000,10000}"

         @"em_dept IN '开发'"

    4、自身:SELF,这个只针对字符数组起作用。如:

         NSArray * test = =[NSArrayarrayWithObjects: @"guangzhou", @"beijing",@"shanghai", nil];

         @"SELF = 'beijing'"

    5、通配符:LIKE

         LIKE 使用?表示一个字符,*表示多个字符,也可以与c、d 连用。如:

         @"car.name LIKE '?he?'" //四个字符中,中间为he

         @"car.name LIKE '*jp'" //以jp结束

    6、正则表达式:MATCHES

    如:

    NSString *regex = @"^E.+e$";    //以E 开头,以e 结尾的字符。

    NSPredicate *pre= [NSPredicate predicateWithFormat: @"SELF MATCHES%@", regex];

    if([pre evaluateWithObject: @"Employee"]){

        NSLog(@"matches YES");

    }else{

        NSLog(@"matches NO");

    }

    7、逻辑运算符:AND、OR、NOT

    如:

         @"employee.name = 'john' AND employee.age = 28"

    8、占位符:

    NSPredicate *preTemplate = [NSPredicate predicateWithFormat: @"name==$NAME"];

    NSDictionary *dic = [NSDictionary dictionaryWithObjectsAndKeys: @"Name1", @"NAME",nil];

    NSPredicate *pre = [preTemplate predicateWithSubstitutionVariables: dic];

        占位符就是字典对象里的key,因此你可以有多个占位符,只要key 不一样就可以了。

    7.1.1.2 代码编写方法

    查询不到结果写法

    //    NSPredicate*predicate=[NSPredicate predicateWithFormat: @"province LIKE '%@?' AND cityLIKE '%@?' AND county = %@", tempEntity. province, tempEntity.city, tempEntity.county];

    可查询到结果写法:

    NSString * predStr = [NSString stringWithFormat: @"province LIKE \'%@?\' AND city LIKE

    \'%@?\' AND county = \'%@\'", tempEntity. province, tempEntity.city, tempEntity.county];

    NSPredicate* predicate=[NSPredicate predicateWithFormat: predStr];

    NSString * predStr = [NSString stringWithFormat: @"province LIKE \'%@%%\' AND city LIKE

    \'%@%%\' AND county = \'%@\'", tempEntity.province, tempEntity.city, tempEntity.county];

    7.1.2 常规查询

    //查询  

    - (IBAction)query:(id)sender {  

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

        NSEntityDescription* user=[NSEntityDescription entityForName: @"User" inManagedObjectContext: _myAppDelegate.managedObjectContext];  

        [request setEntity: user];  

    //    NSSortDescriptor* sortDescriptor = [[NSSortDescriptor alloc] initWithKey: @"name" ascending: YES];  

    //    NSArray* sortDescriptions = [[NSArray alloc] initWithObjects: sortDescriptor, nil];  

    //    [request setSortDescriptors: sortDescriptions];  

    //    [sortDescriptions release];  

    //    [sortDescriptor release];  

        NSError* error = nil;  

        NSMutableArray* mutableFetchResult = [[_myAppDelegate.managedObjectContext executeFetchRequest: request error: &error] mutableCopy];  

        if (mutableFetchResult==nil) {  

            NSLog(@"Error:%@",error);  

        }  

        NSLog(@"The count of entry: %i", [mutableFetchResult count]);  

        for (User* user in mutableFetchResult) {  

            NSLog(@"name:%@----age:%@------sex:%@", user.name, user.age, user.sex);  

        }  

        [mutableFetchResult release];  

        [request release];  

    7.1.3 like查询

    查询不到结果写法

    //    NSPredicate*predicate=[NSPredicate predicateWithFormat:@"province LIKE '%@?' AND cityLIKE '%@?' AND county = %@", tempEntity.province, tempEntity.city, tempEntity.county];

    可查询到结果写法:

    NSString * predStr = [NSString stringWithFormat:@"province LIKE \'%@?\' AND city LIKE

    \'%@?\' AND county = \'%@\'", tempEntity.province, tempEntity.city, tempEntity.county];

        NSPredicate* predicate=[NSPredicate predicateWithFormat: predStr];

    NSString * predStr = [NSString stringWithFormat: @"province LIKE \'%@%%\' AND city LIKE

    \'%@%%\' AND county = \'%@\'", tempEntity.province, tempEntity.city, tempEntity.county];

    7.1.4 多条件查询

    predicate = [NSPredicate predicateWithFormat: @"(salesMan = %@) AND (customerName contains %@) AND (customerSex = %@) AND (createdDate >= %d) AND (createdDate <= %d)",[[NSUserDefaults standardUserDefaults] objectForKey: kDefaultUsernameKey], custName, custSex, fromTime, toTime];

    7.1.5 大批量查询AsynchronousFetching

    7.1.5.1 Asynchronous Fetching简介

    iOS8: Core Data and Asynchronous Fetching

    http://code.tutsplus.com/tutorials/ios-8-core-data-and-asynchronous-fetching--cms-22241

            Asynchronous Fetching的加入依然是为了解决CoreData读取海量数据所带来的问题。通过使用Asynchronous Fetching,我们可以在抓取数据的同时不阻塞占用NSManagedObjectContext ,并可以随时取消抓取行为,随时跟踪抓取数据的进度。

            设想我们平时用 NSFetchRequest 抓取数据的时候,我们会先用NSManagedObjectContext 的 executeFetchRequest:error: 方法传入一个NSFetchRequest ,然后请求会被发送到 NSPersistentStore ,然后执行一段时间后返回一个数组,在 NSManagedObjectContext 更新后,这个数组被当做executeFetchRequest:error: 的返回值返回到我们这里。

            而Asynchronous Fetching则不同,当我们将一个NSAsynchronousFetchRequest 对象传入 executeRequest:error: 方法后会立即返回一个“未来的” NSAsynchronousFetchResult 。NSAsynchronousFetchRequest 初始化时需要传入两个参数赋值给属性:

        1. completionBlock 属性,允许我们在抓取完成后执行回调block;

        2. fetchRequest 属性,类型是 NSFetchRequest 。也即是说虽然是异步抓取,其实我们用的还是以前的 NSFetchRequest ,当 NSFetchRequest 抓取结束后会更新 NSManagedObjectContext ,这也就意味着NSManagedObjectContext 的并发类型只能是NSPrivateQueueConcurrencyType 或 NSMainQueueConcurrencyType。

            于是当我们用 NSAsynchronousFetchRequest 抓取数据时,我们会先用NSManagedObjectContext 的 executeRequest:error: 方法传入一个NSAsynchronousFetchRequest ,这个方法在 NSManagedObjectContext 上执行时, NSManagedObjectContext 会立即制造并返回一个NSAsynchronousFetchResult ,同时 NSAsynchronousFetchRequest 会被发送到NSPersistentStore 。你现在可以继续编辑这个NSManagedObjectContext 中的 NSManagedObject ,等到NSPersistentStore 执行请求完毕时会将结果返回给NSAsynchronousFetchResult的 finalResult 属性,更新NSManagedObjectContext ,执行 NSAsynchronousFetchRequest 的回调block。

    举个栗子:

    let request = NSFetchRequest(entityName: "MyEntity")         

    let async = NSAsynchronousFetchRequest(fetchRequest: request){             

        (id result)in             

        if result.finalResult {                 

            //TODO..             

        }         

    }

            Swift代码很简洁,并用了尾随闭包语法,看不懂的朋友也不用着急,知道NSAsynchronousFetchRequest 大概的用法就行。之前提到过 NSAsynchronousFetchRequest 能在抓取数据的过程中跟踪进度,于是乎 NSProgress 登场了!一行代码顶十句话:

    let request = NSFetchRequest(entityName: "MyEntity") 

    var asyncResult:NSPersistentStoreResult! 

    let async = NSAsynchronousFetchRequest(fetchRequest: request){             

        (id result)in             

        if result.finalResult {                 

            //TODO..             

        }         

    }

    let progress = NSProgress(totalUnitCount: 1) 

    progress.becomeCurrentWithPendingUnitCount(1) 

    managedObjectContext?.performBlock{             

        [unowned self]in             

        let error = NSErrorPointer()             

        asyncResult = self.managedObjectContext?.executeRequest(async, error: error)         

    progress.resignCurrent()

            而取消获取数据只需要取消 NSProgress 就可以了!取消行为会沿着数的根节点蔓延到叶子。

    progress.cancel()

            可以在 cancellationHandler 属性设置取消后执行的block,这里不再多说。

    7.1.5.2 代码示例一

    - (void)viewDidLoad{

        [super viewDidLoad];

        // Helpers

        __weak TSPViewController *weakSelf = self;

        // Initialize Fetch Request

        NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] initWithEntityName: @"TSPItem"];

        // Add Sort Descriptors

        [fetchRequest setSortDescriptors: @[[NSSortDescriptor sortDescriptorWithKey: @"createdAt" ascending:YES]]];

        // Initialize Asynchronous Fetch Request

        NSAsynchronousFetchRequest *asynchronousFetchRequest = [[NSAsynchronousFetchRequest alloc] initWithFetchRequest: fetchRequest completionBlock: ^(NSAsynchronousFetchResult *result) {

            dispatch_async(dispatch_get_main_queue(),^{

                // Process Asynchronous Fetch Result

                [weakSelf processAsynchronousFetchResult: result];

            });

        }];

        // Execute Asynchronous Fetch Request

        [self.managedObjectContext performBlock:^{

            // Execute Asynchronous Fetch Request

            NSError *asynchronousFetchRequestError = nil;

            NSAsynchronousFetchResult *asynchronousFetchResult = (NSAsynchronousFetchResult*)[weakSelf.managedObjectContext executeRequest: asynchronousFetchRequest error: &asynchronousFetchRequestError];

            if(asynchronousFetchRequestError) {

                NSLog(@"Unable to execute asynchronous fetch result.");

                NSLog(@"%@, %@", asynchronousFetchRequestError, asynchronousFetchRequestError.localizedDescription);

            }

        }];

    }

    7.1.5.3 代码示例二

    -(void) getEntityArrayAsynInContext:(NSManagedObjectContext *)context WithEntityName:(NSString *)entityName WithSortDescriptorArray:(NSArray *) sortArray WithPredicate: (NSPredicate *) pred WithPage: (HJDataPage *)page WithCompletionBlock:(HJCoreDataAsynFetchCallbackBlock)block

    {

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

        NSAsynchronousFetchRequest *asynRequest = [[NSAsynchronousFetchRequest alloc] initWithFetchRequest: request completionBlock: ^(NSAsynchronousFetchResult*result){

           NSArray *entityArray = result.finalResult;

           if(block) {

               block(entityArray);

           }

        }];

        if(!entityName) {

           return;

        }

        NSEntityDescription * entity = [NSEntityDescription entityForName: entityName inManagedObjectContext: context];

       [request setEntity: entity];

        if(sortArray) {

           [request setSortDescriptors: sortArray];

        }

        if(pred) {

           [request setPredicate: pred];

        }

        if(page) {

           [request setFetchLimit: page.size];

           [request setFetchOffset: page.pageIndex * page.size];

        }

        //   NSError * error = nil;

        __block idweakContext = context;

        __block idweakAsynRequest = asynRequest;

        //   [context executeRequest:<#(NSPersistentStoreRequest *)#>error:<#(NSError *__autoreleasing *)#>]

        // Execute Asynchronous Fetch Request

       [context performBlock:^{

           // Execute Asynchronous Fetch Request

           NSError *asynchronousFetchRequestError = nil;

           NSAsynchronousFetchResult *asynchronousFetchResult = (NSAsynchronousFetchResult *)[weakContext executeRequest: weakAsynRequest error: &asynchronousFetchRequestError];

           if(asynchronousFetchRequestError) {

               NSLog(@"Unable to execute asynchronous fetch result.");

               NSLog(@"%@, %@", asynchronousFetchRequestError, asynchronousFetchRequestError.localizedDescription);

           }

        }];

    }

    7.2 新增对象

    7.2.1 新建记录

    ios中的coredata的使用

    http://blog.csdn.net/chen505358119/article/details/9334831

    //插入数据  

    - (IBAction)addIntoDataSource:(id)sender {  

        User* user=(User *)[NSEntityDescription insertNewObjectForEntityForName: @"User" inManagedObjectContext: self.myAppDelegate.managedObjectContext];  

        [user setName: _nameText.text];  

        [user setAge: [NSNumber numberWithInteger: [_ageText.text integerValue]]];  

        [user setSex: _sexText.text];  

        NSError* error;  

        BOOL isSaveSuccess = [_myAppDelegate.managedObjectContext save: &error];  

        if (!isSaveSuccess) {  

            NSLog(@"Error:%@", error);  

        }else{  

            NSLog(@"Save successful!");  

        }  

    7.2.2 新增临时实体对象实例

    NSEntityDescription * entity = [NSEntityDescription entityForName: NSStringFromClass([HJUserInfoEntity class]) inManagedObjectContext:[[HJDataModelCoreDataStorage shareInstance] mainThreadManagedObjectContext]];

        HJUserInfoEntity * userTmpEntity = [[HJUserInfoEntity alloc] initWithEntity: entity insertIntoManagedObjectContext: nil];

    7.3 更新对象

    7.3.1 常规更新记录

    //更新  

    - (IBAction)update: (id)sender {  

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

        NSEntityDescription* user=[NSEntityDescription entityForName: @"User" inManagedObjectContext: _myAppDelegate.managedObjectContext];  

        [request setEntity: user];  

        //查询条件  

        NSPredicate* predicate = [NSPredicate predicateWithFormat: @"name==%@", @"chen"];  

        [request setPredicate: predicate];  

        NSError* error=nil;  

        NSMutableArray* mutableFetchResult=[[_myAppDelegate.managedObjectContext executeFetchRequest: request error: &error] mutableCopy];  

        if (mutableFetchResult==nil) {  

            NSLog(@"Error:%@", error);  

        }  

        NSLog(@"The count of entry: %i", [mutableFetchResult count]);  

        //更新age后要进行保存,否则没更新  

        for (User *user in mutableFetchResult) {  

            [user setAge: [NSNumber numberWithInt: 12]];  

        }  

        [_myAppDelegate.managedObjectContext save: &error];  

        [mutableFetchResult release];  

        [request release];  

    }  

    7.3.2 大批量更新Batch Updates

            在CoreData中想要更新大量数据,我们往往要将大量修改后的NSManagedObject 加载到 NSManagedObjectContext 中并保存,这会占用大量内存,试想想在iPhone这样的内存有限的移动设备上将是个灾难,数据有可能丢失。你可能会采取批处理的方式,即一小批一小批的更新NSManagedObject并保存到 NSManagedObjectContext 中,但这样会花费很多时间,用户体验较差。

            为了解决这个问题,苹果在 NSManagedObjectContext 加入了一个新的方法:executeRequest:error:,它接受一个 NSPersistentStoreRequest 类型的参数,返回类型为NSPersistentStoreResult 。

            关于 NSPersistentStoreRequest 有些人可能比较熟悉,它是NSFetchRequest 、NSSaveChangesRequest、NSBatchUpdateRequest和 NSAsynchronousFetchRequest 的基类。后两个类是这次iOS8新加的,也是这篇文章将要讨论的内容。

            NSPersistentStoreResult 是一个新加入的类,它也是一个基类,而且是抽象类,这个类作为executeRequest:error: 返回内容的父类,相当于一个接口,它目前有两个子类 NSPersistentStoreAsynchronousResult 和NSBatchUpdateResult 。

            你大概猜到了, NSBatchUpdateResult 对应着前面的NSBatchUpdateRequest ,下面说说NSBatchUpdateRequest 。它有点像NSFetchRequest :它允许你指定一个想要更新数据的实体;也可以指定一个affectedStores ,它存储了一个接受更新请求的 NSPersistentStore 数组。(其实它是 NSPersistentStoreRequest 的属性);它也有一个谓词属性来做更新的条件,它跟NSFetchRequest中的谓词一样强大和灵活,类似于SQL的where语句;它允许你指定想要更新的字段,通过 propertiesToUpdate 属性来描述字段更新,它是一个字段,key为 NSPropertyDescription 或属性名字符串,value为 NSExpression 或常量。

            接着谈谈 NSBatchUpdateResult ,它有一个 result 属性和 resultType属性, result 中的内容跟 resultType 有关,可能是成功或者失败,有可能是每行被更新的ID,也可能是被更新的行数。

            需要注意的是,由于 NSBatchUpdateRequest 并不会先将数据存入内存,而是直接操作数据库,所以并不会引起NSManagedObjectContext的同步更新,所以你不仅需要获取NSBatchUpdateResult然后刷新 NSManagedObjectContext 对应的数据和UI界面,还需要保证更新后的数据满足数据库模型上的 validation ,因为 NSManagedObjectContext 没有感知Batch Updates,一些数据验证工作就落在了程序员的身上(你需要写一段代码验证更新后的数据是合法的,用户可不希望在跑步APP上看到自己今天跑步里程是个负数)。一旦有非法数据录入数据库,下次加载并修改 NSManagedObject 的时候就会导致数据验证失败。除了上面提到的这些,还要注意Batch Updates对数据库的操作是乐观锁,也就是假定很少会发生同时存取同一块数据的情况,所以你需要制定一个合理的”merge”策略来应付因同时更新数据产生的冲突。

            Batch Updates的优势在于其效率,在处理上万条数据的时候,它执行的时间跟SQL语句执行时间相当。

    7.4 删除

    7.4.1 常规删除记录

    //删除  

    - (IBAction)del:(id)sender {  

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

        NSEntityDescription *user=[NSEntityDescription entityForName: @"User" inManagedObjectContext: _myAppDelegate.managedObjectContext];  

        [request setEntity: user];  

        NSPredicate *predicate = [NSPredicate predicateWithFormat: @"name==%@", @"chen"];  

        [request setPredicate: predicate];  

        NSError* error=nil;  

        NSMutableArray* mutableFetchResult=[[_myAppDelegate.managedObjectContext executeFetchRequest: request error: &error] mutableCopy];  

        if (mutableFetchResult==nil) {  

            NSLog(@"Error:%@", error);  

        }  

        NSLog(@"The count of entry: %i", [mutableFetchResult count]);  

        for (User* user in mutableFetchResult) {  

            [_myAppDelegate.managedObjectContext deleteObject: user];  

        }  

        if ([_myAppDelegate.managedObjectContext save: &error]) {  

            NSLog(@"Error:%@,%@", error, [error userInfo]);  

        }  

    7.5 多线程数据处理

    7.5.1 在子线程中查询并在主线程中新建并使用

    //根据ObjectID构建实体

    - (NSArray *) buildEntityArrayWithObjectIDArr: (NSArray*) objIDArr

    {

        NSMutableArray *entityMArr = [[NSMutableArray alloc] init];

        NSManagedObject *entity;

        for (NSManagedObjectID *id in objIDArr) {

            entity = [[HJCoreDataStorageInstance getMainThreadManagedObjectContext] objectWithID: id];

            [entityMArr addObject: entity];

        }

        return entityMArr;

    }

    -(void)getEntityArrayInUserPageWithUid: (long)uid WithPage: (HJDataPage *)page WithCompleteBlock: (HJEntityCommonCallbackBlock)block

    {

        __block HJResultData*reData;

        dispatch_async(_hjQueryQueue, ^{

            NSArray *arr = [HJCoreDataStorageInstance getInfoEntityArrayInManagedObjectContext:[HJCoreDataStorageInstance getQueryManageObjectContext] WithUserId: [NSString stringWithFormat:@"%ld", uid] WithPage: page];

            NSMutableArray *objIDMArr = [[NSMutableArray alloc] init];

            for (HJInfoEntity *entity in arr) {

                [objIDMArr addObject: entity.objectID];

            }

            dispatch_async(dispatch_get_main_queue(), ^{

                NSArray *entityArr = [self buildEntityArrayWithObjectIDArr: objIDMArr];

                if(block) {

                    reData = [[HJResultData alloc] initWithCode: HJError_Success WithErrMsg: nil WithData: entityArr];

                    block(reData);

                }

            });

        });

    }

    8 数据库Entity升级

    8.1 轻量级数据迁移

    8.1.1 支持场景

    官方文档中介绍如下的改变支持轻量级迁移:

                为Entity简单的添加一个属性

                为Entity移除一个属性

                属性值由 Optional<->Non-optional 之间转换

                为属性设置Default Value

                重命名Entity或者Attribute

                增加一个新的relationship 或者删除一个已经存在的relationship

                重命名relationship

                改变relationship to-one<-> to-many 等

                 增加,删除Entities

                增加新的 Parent 或者Child Entity

                从Hierarchy中移除Entities

            轻量级迁移不支持合并Entity的层级:比如在旧的Model中两个已知的Entities没有共享一个共同的Parent Entity,那么在新的Model中它们也不能够共享一个共同的Parent Entity。

    8.1.2 步骤

        1.升级数据库模型:选中你的mydata.xcdatamodeld文件,选择菜单editor->Add Model Version 比如取名:mydata2.xcdatamodel;

        2.设置当前版本:选择上级mydata.xcdatamodeld ,在inspector中的Versioned Core Data Model选择Current模版为mydata2(inspector界面,即为XCode工作区右侧工具栏);

        3.修改新数据模型mydata2,在新的文件上添加,修改或删除字段及表;

        4.在程序启动时添加如下代码:

    NSDictionary *optionsDictionary = [NSDictionary dictionaryWithObjectsAndKeys: [NSNumber numberWithBool: YES], NSMigratePersistentStoresAutomaticallyOption, [NSNumber numberWithBool: YES], NSInferMappingModelAutomaticallyOption, nil];

    if (![persistentStoreCoordinator addPersistentStoreWithType: NSSQLiteStoreType configuration: nil

    URL: storeUrl options: optionsDictionary error: &error]) {

        NSLog(@”failed to add persistent store with type to persistent store coordinator”);

    }

    5.重启一下XCode

    8.2 参考文档

    Core Data模型版本升级和数据迁移(-)简介

    http://my.oschina.net/zhmsong/blog/147920

    Core Data模型版本升级和数据迁移(二)轻量级数据迁移

    http://my.oschina.net/zhmsong/blog/148021

    Core Data模型版本升级和数据迁移(三)映射概述

    http://my.oschina.net/zhmsong/blog/148209

    Core Data模型版本升级和数据迁移(四)迁移过程

    http://my.oschina.net/zhmsong/blog/148940?fromerr=vlMQCpiW

    (Good)iOS coredata数据库升级 时报Can’tfind model for source store

    http://www.ithao123.cn/content-8093461.html

    iOS CoreData应用升级需要注意的地方

    http://www.oschina.net/question/565065_64657

    iOS App升级安装- CoreData数据库升级

    http://blog.csdn.net/wang9834664/article/details/8203177

    CoreData的数据迁移

    http://www.tuicool.com/articles/QJVVr2

    Core Data版本迁移经验总结

    http://www.tuicool.com/articles/B3YNNj

    (good)coredata数据迁移——有截图

    http://blog.sina.com.cn/s/blog_51a995b70102v3kj.html

    ios coredata error Can't find model for source store[duplicate]

    http://stackoverflow.com/questions/16119689/ios-coredata-error-cant-find-model-for-source-store

    core data can't find model for source store - what did myold store look like?

    http://stackoverflow.com/questions/3585825/core-data-cant-find-model-for-source-store-what-did-my-old-store-look-like

    9 参考链接

    (good)CoreData多线程下NSManagedObjectContext的使用

    http://www.aiuxian.com/article/p-2533636.html

    (Good)Multi-ContextCoreData

    http://www.cocoanetics.com/2012/07/multi-context-coredata/

    Adventures in Multithreaded Core Data

    http://www.slideshare.net/Inferis/adventures-in-multithreaded-core-data

    NSPredicate条件查询或过虑

    http://blog.csdn.net/fengsh998/article/details/8125263

    IOS CoreData多表查询(下)

    http://blog.csdn.net/fengsh998/article/details/8123392

    Core Data编程指南(翻译)

    http://blog.csdn.net/guchengluoye/article/details/7782999

    iOS8: Core Data and Asynchronous Fetching

    http://code.tutsplus.com/tutorials/ios-8-core-data-and-asynchronous-fetching--cms-22241

    iOS Core data多线程并发访问的问题

    http://www.cnblogs.com/rolandash/p/3769127.html

    多线程操作数据库(CoreData)

    http://linwwwei.iteye.com/blog/1296559

    Multi-Context CoreData with batch fetch by relationship

    http://stackoverflow.com/questions/19786569/multi-context-coredata-with-batch-fetch-by-relationship

    Core Data Tutorial: Multiple Managed Object Contexts

    http://www.raywenderlich.com/84642/multiple-managed-object-contexts-in-core-data-tutorial

    CoreData多线程处理大量数据同步时的操作

    http://blog.csdn.net/leikezhu1981/article/details/46296173

    深入浅出Cocoa之Core Data(1)  -框架详解

    http://www.cppblog.com/ipzyh/articles/CoreData.html

    iOS开发过程中使用CoreData应避免的十个错误

    http://blog.jobbole.com/60025/

    crash on coredata ios8

    http://stackoverflow.com/questions/25863607/crash-on-coredata-ios8

    Exception thrown in NSOrderedSet generated accessors

    http://stackoverflow.com/questions/7385439/exception-thrown-in-nsorderedset-generated-accessors

    Migration Core Data error code 134130

    http://stackoverflow.com/questions/11130928/migration-core-data-error-code-134130

    iOS crash with Core Data

    http://stackoverflow.com/questions/29653545/ios-crash-with-core-data

    Core Data; Cocoa error 134100

    http://stackoverflow.com/questions/5517129/core-data-cocoa-error-134100

    相关文章

      网友评论

      本文标题:【IOS开发高级系列】CoreData专题

      本文链接:https://www.haomeiwen.com/subject/nhdvhftx.html