【IOS開發(fā)高級系列】CoreData專題

1 CoreData運行機制

1.1 CoreData總體架構(gòu)

1.1.1 CoreData架構(gòu)圖

CoreData架構(gòu)圖:

1.1.2 主要術(shù)語

1兄一,Managed Object Model

????????Managed Object Model 是描述應(yīng)用程序的數(shù)據(jù)模型,這個模型包含實體(Entity),特性(Property),讀取請求(Fetch Request)等锤岸。(下文都使用英文術(shù)語。)

2,Managed Object Context

????????Managed Object Context 參與對數(shù)據(jù)對象進行各種操作的全過程击敌,并監(jiān)測數(shù)據(jù)對象的變化,以提供對 undo/redo 的支持及更新綁定到數(shù)據(jù)的 UI拴事。

3沃斤,Persistent Store Coordinator

????????Persistent Store Coordinator 相當于數(shù)據(jù)文件管理器,處理底層的對數(shù)據(jù)文件的讀取與寫入刃宵。一般我們無需與它打交道衡瓶。

4,Managed Object

????????Managed Object 數(shù)據(jù)對象牲证,與 Managed Object Context 相關(guān)聯(lián)哮针。


1.1.3 初始化機制

????????1,應(yīng)用程序先創(chuàng)建或讀取模型文件(后綴為xcdatamodeld)生成 NSManagedObjectModel 對象坦袍。Document應(yīng)用程序是一般是通過 NSDocument 或其子類 NSPersistentDocument)從模型文件(后綴為 xcdatamodeld)讀取十厢。

????????2,然后生成 NSManagedObjectContext 和 NSPersistentStoreCoordinator 對象捂齐,前者對用戶透明地調(diào)用后者對數(shù)據(jù)文件進行讀寫蛮放。

????????3,NSPersistentStoreCoordinator 負責從數(shù)據(jù)文件(xml, sqlite,二進制文件等)中讀取數(shù)據(jù)生成 Managed Object奠宜,或保存 Managed Object 寫入數(shù)據(jù)文件包颁。

????????4,NSManagedObjectContext 參與對數(shù)據(jù)進行各種操作的整個過程压真,它持有 Managed Object徘六。我們通過它來監(jiān)測 Managed Object。監(jiān)測數(shù)據(jù)對象有兩個作用:支持 undo/redo 以及數(shù)據(jù)綁定榴都。這個類是最常被用到的待锈。

????????5,Array Controller, Object Controller, Tree Controller 這些控制器一般與 NSManagedObjectContext 關(guān)聯(lián)嘴高,因此我們可以通過它們在 nib 中可視化地操作數(shù)據(jù)對象竿音。


1.2 Model Classes

????????模型有點像數(shù)據(jù)庫的表結(jié)構(gòu),里面包含 Entry拴驮, 實體又包含三種 Property:Attribute(屬性)春瞬,RelationShip(關(guān)系), Fetched Property(讀取屬性)套啤。Model class 的名字多以 "Description" 結(jié)尾宽气。我們可以看出:模型就是描述數(shù)據(jù)類型以及其關(guān)系的随常。

????????主要的 Model class 有:

????1)Entity - NSEntityDescription

????????Entity 相當于數(shù)據(jù)庫中的一個表,它描述一種抽象數(shù)據(jù)類型萄涯,其對應(yīng)的類為 NSManagedObject 或其子類绪氛。

????????NSEntityDescription 常用方法:

+insertNewObjectForEntityForName:inManagedObjectContext:工廠方法,根據(jù)給定的 Entity 描述涝影,生成相應(yīng)的 NSManagedObject 對象枣察,并插入 ManagedObjectContext 中。

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

-attributesByName 以名字為 key燃逻, 返回 Entity 中對應(yīng)的Attributes

-relationshipsByName 以名字為 key序目, 返回 Entity 中對應(yīng)的Relationships


????2)Property - NSPropertyDescription

????????Property 為 Entity 的特性,它相當于數(shù)據(jù)庫表中的一列伯襟,或者 XML 文件中的 value-key 對中的 key猿涨。它可以描述實體數(shù)據(jù)(Attribute),Entity之間的關(guān)系(RelationShip)姆怪,或查詢屬性(Fetched Property)叛赚。

????> Attribute - NSAttributeDescription

????????Attribute 存儲基本數(shù)據(jù),如 NSString,

????????NSNumber or NSDate 等片效。它可以有默認值红伦,也可以使用正則表達式或其他條件對其值進行限定英古。一個屬性可以是 optional 的淀衣。

????> Relationship -NSRelationshipDescription

????????Relationship 描述 Entity忌傻,Property 之間的關(guān)系扔罪,可以是一對一,也可以是一對多的關(guān)系浙踢。

????> Fetched Property - NSFetchedPropertyDescription

????????Fetched Property 根據(jù)查詢謂詞返回指定 Entity 的符合條件的數(shù)據(jù)對象唠叛。


1.3 CoreData操作對象

1.3.1 NSManagedObject

> Managed Object - NSManagedObject

????????Managed Object 表示數(shù)據(jù)文件中的一條記錄只嚣,每一個 Managed Object 在內(nèi)存中對應(yīng) Entity 的一個數(shù)據(jù)表示。Managed Object 的成員為 Entity 的 Property 所描述艺沼。

????????每一個 Managed Object 都有一個全局 ID(類型為:NSManagedObjectID)册舞。Managed Object 會附加到一個 Managed Object Context,我們可以通過這個全局 ID 在 Managed Object Context 查詢對應(yīng)的 Managed Object障般。


NSManagedObject 常用方法

-entity獲取其Entity

-objectID獲取其Managed Object? ID

-valueForKey:獲取指定 Property 的值

-setValue: forKey:設(shè)定指定 Property 的值


1.3.2 NSManagedObjectContext

> Managed Object Context -NSManagedObjectContext

????????Managed Object Context 的作用相當重要调鲸,對數(shù)據(jù)對象進行的操作都與它有關(guān)。當創(chuàng)建一個數(shù)據(jù)對象并插入 Managed Object Context 中挽荡,Managed Object Context 就開始跟蹤這個數(shù)據(jù)對象的一切變動藐石,并在合適的時候提供對 undo/redo 的支持,或調(diào)用 Persistent Store Coordinato 將變化保存到數(shù)據(jù)文件中去定拟。

????????通常我們將 controller 類(如:NSArrayController于微,NSTreeController)或其子類與 Managed Object Context 綁定,這樣就方便我們動態(tài)地生成,獲取數(shù)據(jù)對象等株依。


NSManagedObjectContext 常用方法

-save:將數(shù)據(jù)對象保存到數(shù)據(jù)文件

-objectWithID:查詢指定 Managed Object ID 的數(shù)據(jù)對象

-deleteObject:將一個數(shù)據(jù)對象標記為刪除驱证,但是要等到 Context 提交更改時才真正刪除數(shù)據(jù)對象

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

-lock加鎖勺三,常用于多線程以及創(chuàng)建事務(wù)雷滚。同類接口還有:-unlock and -tryLock

-rollback還原數(shù)據(jù)文件內(nèi)容

-reset清除緩存的 Managed Objects。只應(yīng)當在添加或刪除 Persistent Stores 時使用

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

-assignObject: toPersistantStore:由于 Context 可以管理從不同數(shù)據(jù)文件而來的數(shù)據(jù)對象吗坚,這個接口的作用就是指定數(shù)據(jù)對象的存儲數(shù)據(jù)文件(通過指定 PersistantStore 實現(xiàn))

-executeFetchRequest: error:執(zhí)行 Fetch Request 并返回所有匹配的數(shù)據(jù)對象


1.3.3 NSPersistentStoreCoordinator

> Persistent Store Coordinator -NSPersistentStoreCoordinator

????????使用 Core Data document 類型的應(yīng)用程序祈远,通常會從磁盤上的數(shù)據(jù)文中中讀取或存儲數(shù)據(jù),這寫底層的讀寫就由 Persistent Store Coordinator 來處理商源。一般我們無需與它直接打交道來讀寫文件车份,Managed Object Context 在背后已經(jīng)為我們調(diào)用 Persistent Store Coordinator 做了這部分工作。


NSPersistentStoreCoordinator 常用方法

-addPersistentStoreForURL:configuration:URL:options:error: 裝載數(shù)據(jù)存儲牡彻,對應(yīng)的卸載數(shù)據(jù)存儲的接口為-removePersistentStore:error:

-migratePersistentStore:toURL:options:withType:error: 遷移數(shù)據(jù)存儲扫沼,效果與 "save as"相似,但是操作成功后庄吼,遷移前的數(shù)據(jù)存儲不可再使用

-managedObjectIDForURIRepresentation: 返回給定 URL所指示的數(shù)據(jù)存儲的 object id缎除,如果找不到匹配的數(shù)據(jù)存儲則返回nil

-persistentStoreForURL: 返回指定路徑的Persistent? Store

-URLForPersistentStore: 返回指定 Persistent Store 的存儲路徑


1.3.4 NSPersistentDocument

> Persistent Document -NSPersistentDocument

????????NSPersistentDocument是 NSDocument 的子類。 multi-document Core Data 應(yīng)用程序使用它來簡化對 Core Data 的操作总寻。通常使用NSPersistentDocument 的默認實現(xiàn)就足夠了器罐,它從 Info.plist 中讀取 Document types 信息來決定數(shù)據(jù)的存儲格式(xml,sqlite, binary)。


NSPersistentDocument 常用方法

-managedObjectContext返回文檔的 Managed Object Context渐行,在多文檔應(yīng)用程序中轰坊,每個文檔都有自己的 Context。

-managedObjectModel返回文檔的Managed Object? Model


1.4 查詢Fetch Requests

????????Fetch Requests 相當于一個查詢語句祟印,你必須指定要查詢的 Entity肴沫。我們通過 Fetch Requests 向 Managed Object Context 查詢符合條件的數(shù)據(jù)對象,以 NSArray 形式返回查詢結(jié)果蕴忆,如果我們沒有設(shè)置任何查詢條件颤芬,則返回該 Entity 的所有數(shù)據(jù)對象。我們可以使用謂詞來設(shè)置查詢條件套鹅,通常會將常用的 Fetch Requests 保存到 dictionary 以重復(fù)利用站蝠。

示例:

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];

????????在上面代碼中,我們查詢在指定日期之后創(chuàng)建的 post芋哭,并將查詢結(jié)果按照 title 排序返回沉衣。


NSFetchRequest 常用方法

-setEntity:設(shè)置你要查詢的數(shù)據(jù)對象的類型(Entity)

-setPredicate:設(shè)置查詢條件

-setFetchLimit:設(shè)置最大查詢對象數(shù)目

-setSortDescriptors:設(shè)置查詢結(jié)果的排序方法

-setAffectedStores:設(shè)置可以在哪些數(shù)據(jù)存儲中查詢


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

預(yù)定義查詢語句

????????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 開發(fā)技巧

3.3.1 modelURL的文件名必須與創(chuàng)建的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;

??? }


??? //此處文件名必須與創(chuàng)建的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 數(shù)據(jù)變化通知

// 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架構(gòu)

5.2.1 方案設(shè)計思想

(Good)Multi-ContextCoreData

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


此方案采用典型的三層架構(gòu):

父Context為后臺寫隊列Context减牺;

子Context為MainThread頁面同步Context豌习;

孫Context為數(shù)據(jù)讀取的子線程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];

??????? }


??????? //內(nèi)存數(shù)據(jù)優(yōu)先

??????? _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];

??????? //內(nèi)存數(shù)據(jù)優(yōu)先

??????? _queryManagedObjectContext.mergePolicy = NSMergeByPropertyObjectTrumpMergePolicy;

??? }


??? return _queryManagedObjectContext;

}


5.2.2.2 ChangesMerge監(jiān)聽

- (void) commonInit

{

??? _pageMax = [[HJDataPage alloc] init];

??? _pageMax.pageIndex = 0;

??? _pageMax.size = 1000;// INT_MAX;// 不能用NSIntegerMax,其在6以上是64bit數(shù)據(jù)肥隆,會引起索引值越界;

??? _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對應(yīng)一個StoreCoordinator的CoreData架構(gòu)

5.3.1 方案設(shè)計思路

????????對于多線程環(huán)境下得CoreData操作既荚,一個Context對應(yīng)一個StoreCoordinator的方案最安全,但是內(nèi)存消耗也相應(yīng)更多栋艳,因此除非必要恰聘,不要輕易使用此方案。


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;

??????? //內(nèi)存數(shù)據(jù)優(yōu)先

??????? 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監(jiān)聽

- (void) commonInit

{

??? _pageMax = [[HJDataPage alloc] init];

??? _pageMax.pageIndex = 0;

??? _pageMax.size = 10000;// INT_MAX;// 不能用NSIntegerMax吸占,其在6以上是64bit數(shù)據(jù)晴叨,會引起索引值越界;

??? _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 數(shù)據(jù)庫操作

7.1 查詢

7.1.1 NSPredicate

????????NSPredicate用于查詢和過濾在SQL中作為查詢條件通常用WHERE,但在CORE DATA中作為查詢條件就可以用到NSPredicate. NSPredicate 不單可以和CORE DATA中的FetchRequest 配合使用矾屯。也可以與NSArray配合使用兼蕊。

7.1.1.1 NSPredicate中支持的關(guān)鍵詞和條件符

????1、>,<,>=,<=,= 比較運算符件蚕。如:

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

????2孙技、字符串操作(包含):BEGINSWITH、ENDSWITH排作、CONTAINS牵啦,如:

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

?????@"employee.name ENDSWITH[c] '夢'"?//以夢結(jié)束的員工

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

????????注:[c]不區(qū)分大小寫[d]不區(qū)分發(fā)音符號即沒有重音符號[cd]既不區(qū)分大小寫,也不區(qū)分發(fā)音符號妄痪。

3哈雏、范圍:IN?,BWTEEN拌夏,如:

?????@"salary BWTEEN{5000,10000}"

?????@"em_dept IN '開發(fā)'"

4僧著、自身:SELF履因,這個只針對字符數(shù)組起作用障簿。如:

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

?????@"SELF = 'beijing'"

5、通配符:LIKE

?????LIKE 使用?表示一個字符栅迄,*表示多個字符站故,也可以與c、d 連用毅舆。如:

?????@"car.name LIKE '?he?'" //四個字符中西篓,中間為he

?????@"car.name LIKE '*jp'"?//以jp結(jié)束

6、正則表達式:MATCHES

如:

NSString *regex = @"^E.+e$";????//以E 開頭憋活,以e 結(jié)尾的字符岂津。

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 代碼編寫方法

查詢不到結(jié)果寫法

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


可查詢到結(jié)果寫法:

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 常規(guī)查詢

//查詢??

-?(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查詢

查詢不到結(jié)果寫法

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


可查詢到結(jié)果寫法:

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讀取海量數(shù)據(jù)所帶來的問題。通過使用Asynchronous Fetching茶宵,我們可以在抓取數(shù)據(jù)的同時不阻塞占用NSManagedObjectContext?危纫,并可以隨時取消抓取行為,隨時跟蹤抓取數(shù)據(jù)的進度乌庶。

????????設(shè)想我們平時用?NSFetchRequest?抓取數(shù)據(jù)的時候种蝶,我們會先用NSManagedObjectContext 的?executeFetchRequest:error:?方法傳入一個NSFetchRequest?,然后請求會被發(fā)送到 NSPersistentStore?瞒大,然后執(zhí)行一段時間后返回一個數(shù)組蛤吓,在?NSManagedObjectContext?更新后,這個數(shù)組被當做executeFetchRequest:error:?的返回值返回到我們這里糠赦。

????????而Asynchronous Fetching則不同会傲,當我們將一個NSAsynchronousFetchRequest?對象傳入?executeRequest:error:?方法后會立即返回一個“未來的”?NSAsynchronousFetchResult?。NSAsynchronousFetchRequest?初始化時需要傳入兩個參數(shù)賦值給屬性:

????1. completionBlock?屬性拙泽,允許我們在抓取完成后執(zhí)行回調(diào)block淌山;

????2. fetchRequest?屬性,類型是?NSFetchRequest?顾瞻。也即是說雖然是異步抓取荷荤,其實我們用的還是以前的?NSFetchRequest?,當?NSFetchRequest?抓取結(jié)束后會更新?NSManagedObjectContext?稻薇,這也就意味著NSManagedObjectContext?的并發(fā)類型只能是NSPrivateQueueConcurrencyType 或?NSMainQueueConcurrencyType。

????????于是當我們用?NSAsynchronousFetchRequest?抓取數(shù)據(jù)時暇昂,我們會先用NSManagedObjectContext 的?executeRequest:error:?方法傳入一個NSAsynchronousFetchRequest?瘪校,這個方法在?NSManagedObjectContext?上執(zhí)行時,?NSManagedObjectContext?會立即制造并返回一個NSAsynchronousFetchResult?,同時?NSAsynchronousFetchRequest?會被發(fā)送到NSPersistentStore?。你現(xiàn)在可以繼續(xù)編輯這個NSManagedObjectContext?中的?NSManagedObject?杀迹,等到NSPersistentStore?執(zhí)行請求完畢時會將結(jié)果返回給NSAsynchronousFetchResult的?finalResult?屬性,更新NSManagedObjectContext?绵载,執(zhí)行?NSAsynchronousFetchRequest?的回調(diào)block购裙。

舉個栗子:

let request = NSFetchRequest(entityName: "MyEntity")?????????

let?async = NSAsynchronousFetchRequest(fetchRequest: request){?????????????

????(id result)in?????????????

? ??if?result.finalResult {?????????????????

? ??????//TODO..?????????????

????}?????????

}

????????Swift代碼很簡潔,并用了尾隨閉包語法,看不懂的朋友也不用著急,知道NSAsynchronousFetchRequest 大概的用法就行付翁。之前提到過?NSAsynchronousFetchRequest?能在抓取數(shù)據(jù)的過程中跟蹤進度,于是乎?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()

????????而取消獲取數(shù)據(jù)只需要取消?NSProgress?就可以了晦溪!取消行為會沿著數(shù)的根節(jié)點蔓延到葉子舟肉。

progress.cancel()

????????可以在?cancellationHandler?屬性設(shè)置取消后執(zhí)行的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

//插入數(shù)據(jù)??

-?(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 常規(guī)更新記錄

//更新??

-?(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中想要更新大量數(shù)據(jù),我們往往要將大量修改后的NSManagedObject?加載到?NSManagedObjectContext?中并保存配猫,這會占用大量內(nèi)存腐巢,試想想在iPhone這樣的內(nèi)存有限的移動設(shè)備上將是個災(zāi)難风宁,數(shù)據(jù)有可能丟失饮寞。你可能會采取批處理的方式,即一小批一小批的更新NSManagedObject并保存到?NSManagedObjectContext?中您炉,但這樣會花費很多時間膏蚓,用戶體驗較差千所。

????????為了解決這個問題籽孙,蘋果在?NSManagedObjectContext?加入了一個新的方法:executeRequest:error:玻熙,它接受一個?NSPersistentStoreRequest?類型的參數(shù)悬而,返回類型為NSPersistentStoreResult?锭汛。

????????關(guān)于?NSPersistentStoreRequest?有些人可能比較熟悉笨奠,它是NSFetchRequest?、NSSaveChangesRequest唤殴、NSBatchUpdateRequest和?NSAsynchronousFetchRequest?的基類般婆。后兩個類是這次iOS8新加的,也是這篇文章將要討論的內(nèi)容朵逝。

????????NSPersistentStoreResult?是一個新加入的類蔚袍,它也是一個基類,而且是抽象類配名,這個類作為executeRequest:error:?返回內(nèi)容的父類啤咽,相當于一個接口,它目前有兩個子類 NSPersistentStoreAsynchronousResult?和NSBatchUpdateResult?渠脉。

????????你大概猜到了宇整,?NSBatchUpdateResult?對應(yīng)著前面的NSBatchUpdateRequest?,下面說說NSBatchUpdateRequest?芋膘。它有點像NSFetchRequest?:它允許你指定一個想要更新數(shù)據(jù)的實體鳞青;也可以指定一個affectedStores?霸饲,它存儲了一個接受更新請求的?NSPersistentStore?數(shù)組。(其實它是?NSPersistentStoreRequest?的屬性)臂拓;它也有一個謂詞屬性來做更新的條件厚脉,它跟NSFetchRequest中的謂詞一樣強大和靈活,類似于SQL的where語句埃儿;它允許你指定想要更新的字段器仗,通過?propertiesToUpdate?屬性來描述字段更新融涣,它是一個字段童番,key為?NSPropertyDescription?或?qū)傩悦址瑅alue為?NSExpression?或常量威鹿。

????????接著談?wù)?NSBatchUpdateResult?剃斧,它有一個?result?屬性和?resultType屬性,?result?中的內(nèi)容跟?resultType?有關(guān)忽你,可能是成功或者失敗幼东,有可能是每行被更新的ID,也可能是被更新的行數(shù)科雳。

????????需要注意的是根蟹,由于?NSBatchUpdateRequest?并不會先將數(shù)據(jù)存入內(nèi)存,而是直接操作數(shù)據(jù)庫糟秘,所以并不會引起NSManagedObjectContext的同步更新简逮,所以你不僅需要獲取NSBatchUpdateResult然后刷新?NSManagedObjectContext?對應(yīng)的數(shù)據(jù)和UI界面,還需要保證更新后的數(shù)據(jù)滿足數(shù)據(jù)庫模型上的?validation?尿赚,因為?NSManagedObjectContext?沒有感知Batch Updates散庶,一些數(shù)據(jù)驗證工作就落在了程序員的身上(你需要寫一段代碼驗證更新后的數(shù)據(jù)是合法的,用戶可不希望在跑步APP上看到自己今天跑步里程是個負數(shù))凌净。一旦有非法數(shù)據(jù)錄入數(shù)據(jù)庫悲龟,下次加載并修改?NSManagedObject?的時候就會導(dǎo)致數(shù)據(jù)驗證失敗。除了上面提到的這些冰寻,還要注意Batch Updates對數(shù)據(jù)庫的操作是樂觀鎖须教,也就是假定很少會發(fā)生同時存取同一塊數(shù)據(jù)的情況,所以你需要制定一個合理的”merge”策略來應(yīng)付因同時更新數(shù)據(jù)產(chǎn)生的沖突斩芭。

????????Batch Updates的優(yōu)勢在于其效率轻腺,在處理上萬條數(shù)據(jù)的時候,它執(zhí)行的時間跟SQL語句執(zhí)行時間相當秒旋。


7.4 刪除

7.4.1 常規(guī)刪除記錄

//刪除??

-?(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 多線程數(shù)據(jù)處理

7.5.1 在子線程中查詢并在主線程中新建并使用

//根據(jù)ObjectID構(gòu)建實體

- (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 數(shù)據(jù)庫Entity升級

8.1 輕量級數(shù)據(jù)遷移

8.1.1 支持場景

官方文檔中介紹如下的改變支持輕量級遷移:

??????????? 為Entity簡單的添加一個屬性

??????????? 為Entity移除一個屬性

??????????? 屬性值由 Optional<->Non-optional 之間轉(zhuǎn)換

??????????? 為屬性設(shè)置Default Value

??????????? 重命名Entity或者Attribute

??????????? 增加一個新的relationship 或者刪除一個已經(jīng)存在的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.升級數(shù)據(jù)庫模型:選中你的mydata.xcdatamodeld文件煤蚌,選擇菜單editor->Add Model Version 比如取名:mydata2.xcdatamodel耕挨;

????2.設(shè)置當前版本:選擇上級mydata.xcdatamodeld ,在inspector中的Versioned Core Data Model選擇Current模版為mydata2(inspector界面尉桩,即為XCode工作區(qū)右側(cè)工具欄)筒占;

????3.修改新數(shù)據(jù)模型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模型版本升級和數(shù)據(jù)遷移(-)簡介

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


Core Data模型版本升級和數(shù)據(jù)遷移(二)輕量級數(shù)據(jù)遷移

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


Core Data模型版本升級和數(shù)據(jù)遷移(三)映射概述

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


Core Data模型版本升級和數(shù)據(jù)遷移(四)遷移過程

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


(Good)iOS coredata數(shù)據(jù)庫升級 時報Can’tfind model for source store

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


iOS CoreData應(yīng)用升級需要注意的地方

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


iOS App升級安裝- CoreData數(shù)據(jù)庫升級

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


CoreData的數(shù)據(jù)遷移

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


Core Data版本遷移經(jīng)驗總結(jié)

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


(good)coredata數(shù)據(jù)遷移——有截圖

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多線程并發(fā)訪問的問題

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


多線程操作數(shù)據(jù)庫(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多線程處理大量數(shù)據(jù)同步時的操作

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


深入淺出Cocoa之Core Data(1)? -框架詳解

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


iOS開發(fā)過程中使用CoreData應(yīng)避免的十個錯誤

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

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市这橙,隨后出現(xiàn)的幾起案子奏窑,更是在濱河造成了極大的恐慌,老刑警劉巖屈扎,帶你破解...
    沈念sama閱讀 207,113評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件埃唯,死亡現(xiàn)場離奇詭異,居然都是意外死亡鹰晨,警方通過查閱死者的電腦和手機墨叛,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,644評論 2 381
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來模蜡,“玉大人漠趁,你說我怎么就攤上這事∪碳玻” “怎么了闯传?”我有些...
    開封第一講書人閱讀 153,340評論 0 344
  • 文/不壞的土叔 我叫張陵,是天一觀的道長膝昆。 經(jīng)常有香客問我丸边,道長,這世上最難降的妖魔是什么荚孵? 我笑而不...
    開封第一講書人閱讀 55,449評論 1 279
  • 正文 為了忘掉前任妹窖,我火速辦了婚禮,結(jié)果婚禮上收叶,老公的妹妹穿的比我還像新娘骄呼。我一直安慰自己,他們只是感情好判没,可當我...
    茶點故事閱讀 64,445評論 5 374
  • 文/花漫 我一把揭開白布蜓萄。 她就那樣靜靜地躺著,像睡著了一般澄峰。 火紅的嫁衣襯著肌膚如雪嫉沽。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,166評論 1 284
  • 那天俏竞,我揣著相機與錄音绸硕,去河邊找鬼堂竟。 笑死,一個胖子當著我的面吹牛玻佩,可吹牛的內(nèi)容都是我干的出嘹。 我是一名探鬼主播,決...
    沈念sama閱讀 38,442評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼咬崔,長吁一口氣:“原來是場噩夢啊……” “哼税稼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起垮斯,我...
    開封第一講書人閱讀 37,105評論 0 261
  • 序言:老撾萬榮一對情侶失蹤郎仆,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后甚脉,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體丸升,經(jīng)...
    沈念sama閱讀 43,601評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡铆农,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,066評論 2 325
  • 正文 我和宋清朗相戀三年牺氨,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片墩剖。...
    茶點故事閱讀 38,161評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡猴凹,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出岭皂,到底是詐尸還是另有隱情郊霎,我是刑警寧澤,帶...
    沈念sama閱讀 33,792評論 4 323
  • 正文 年R本政府宣布爷绘,位于F島的核電站书劝,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏土至。R本人自食惡果不足惜购对,卻給世界環(huán)境...
    茶點故事閱讀 39,351評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望陶因。 院中可真熱鬧骡苞,春花似錦、人聲如沸楷扬。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,352評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽烘苹。三九已至躲株,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間镣衡,已是汗流浹背霜定。 一陣腳步聲響...
    開封第一講書人閱讀 31,584評論 1 261
  • 我被黑心中介騙來泰國打工吞琐, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人然爆。 一個月前我還...
    沈念sama閱讀 45,618評論 2 355
  • 正文 我出身青樓站粟,卻偏偏與公主長得像,于是被迫代替她去往敵國和親曾雕。 傳聞我的和親對象是個殘疾皇子奴烙,可洞房花燭夜當晚...
    茶點故事閱讀 42,916評論 2 344

推薦閱讀更多精彩內(nèi)容

  • rljs by sennchi Timeline of History Part One The Cognitiv...
    sennchi閱讀 7,294評論 0 10
  • 我從小時候到現(xiàn)在,一直都有一種隱蔽的愛好剖张,就是看人吵架切诀。 并不是說我喜歡在道德基礎(chǔ)上,去評判出一個孰對孰錯搔弄,我沒有...
    油爆苦瓜閱讀 236評論 0 0
  • 我認為最難過的幅虑,除了痛苦就是離別。 離別是人人都經(jīng)歷過的顾犹,人與人或人與物之間一旦有了情倒庵,就變得難舍難分了。每當離別...
    7db0debe5b84閱讀 324評論 0 1
  • 有句話說得好,聽再多的道理浑玛,還是沒能過好這一生绍申。 為什么? 因為你是葛優(yōu)躺著看的顾彰。 康哥分享一篇關(guān)于自律的文章极阅,我...
    自律的哲仔媽媽閱讀 263評論 0 1
  • 【以終為始】20171010數(shù)里作業(yè)踐行D2 1.用樂高五和湊十的玩法。點數(shù)沒有問題涨享。 2.自己主動要求玩百數(shù)方格...
    王蘭_hope閱讀 465評論 0 0