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

1 CoreData運(yùn)行機(jī)制

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ù)模型,這個(gè)模型包含實(shí)體(Entity),特性(Property),讀取請求(Fetch Request)等。(下文都使用英文術(shù)語。)

2,Managed Object Context

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

3,Persistent Store Coordinator

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

4,Managed Object

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


1.1.3 初始化機(jī)制

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

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

????????3,NSPersistentStoreCoordinator 負(fù)責(zé)從數(shù)據(jù)文件(xml, sqlite,二進(jìn)制文件等)中讀取數(shù)據(jù)生成 Managed Object,或保存 Managed Object 寫入數(shù)據(jù)文件。

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

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


1.2 Model Classes

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

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

????1)Entity - NSEntityDescription

????????Entity 相當(dāng)于數(shù)據(jù)庫中的一個(gè)表,它描述一種抽象數(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 的特性,它相當(dāng)于數(shù)據(jù)庫表中的一列,或者 XML 文件中的 value-key 對中的 key。它可以描述實(shí)體數(shù)據(jù)(Attribute),Entity之間的關(guān)系(RelationShip),或查詢屬性(Fetched Property)。

????> Attribute - NSAttributeDescription

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

????????NSNumber or NSDate 等。它可以有默認(rèn)值,也可以使用正則表達(dá)式或其他條件對其值進(jìn)行限定。一個(gè)屬性可以是 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ù)文件中的一條記錄,每一個(gè) Managed Object 在內(nèi)存中對應(yīng) Entity 的一個(gè)數(shù)據(jù)表示。Managed Object 的成員為 Entity 的 Property 所描述。

????????每一個(gè) Managed Object 都有一個(gè)全局 ID(類型為:NSManagedObjectID)。Managed Object 會(huì)附加到一個(gè) Managed Object Context,我們可以通過這個(gè)全局 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 的作用相當(dāng)重要,對數(shù)據(jù)對象進(jìn)行的操作都與它有關(guān)。當(dāng)創(chuàng)建一個(gè)數(shù)據(jù)對象并插入 Managed Object Context 中,Managed Object Context 就開始跟蹤這個(gè)數(shù)據(jù)對象的一切變動(dòng),并在合適的時(shí)候提供對 undo/redo 的支持,或調(diào)用 Persistent Store Coordinato 將變化保存到數(shù)據(jù)文件中去。

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


NSManagedObjectContext 常用方法

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

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

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

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

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

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

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

-undoManager返回當(dāng)前 Context 所使用的NSUndoManager

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

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


1.3.3 NSPersistentStoreCoordinator

> Persistent Store Coordinator -NSPersistentStoreCoordinator

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


NSPersistentStoreCoordinator 常用方法

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

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

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

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

-URLForPersistentStore: 返回指定 Persistent Store 的存儲(chǔ)路徑


1.3.4 NSPersistentDocument

> Persistent Document -NSPersistentDocument

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


NSPersistentDocument 常用方法

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

-managedObjectModel返回文檔的Managed Object? Model


1.4 查詢Fetch Requests

????????Fetch Requests 相當(dāng)于一個(gè)查詢語句,你必須指定要查詢的 Entity。我們通過 Fetch Requests 向 Managed Object Context 查詢符合條件的數(shù)據(jù)對象,以 NSArray 形式返回查詢結(jié)果,如果我們沒有設(shè)置任何查詢條件,則返回該 Entity 的所有數(shù)據(jù)對象。我們可以使用謂詞來設(shè)置查詢條件,通常會(huì)將常用的 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ù)存儲(chǔ)中查詢


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


避免允許空值,使用默認(rèn)值代替

????????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è)計(jì)思想

(Good)Multi-ContextCoreData

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


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

父Context為后臺(tái)寫隊(duì)列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ù),會(huì)引起索引值越界;

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

5.3.1 方案設(shè)計(jì)思路

????????對于多線程環(huán)境下得CoreData操作,一個(gè)Context對應(yīng)一個(gè)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ù),會(huì)引起索引值越界;

??? _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、>,<,>=,<=,= 比較運(yùn)算符。如:

?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ā)音符號(hào)即沒有重音符號(hào)[cd]既不區(qū)分大小寫,也不區(qū)分發(fā)音符號(hào)。

3、范圍:IN?,BWTEEN,如:

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

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

4、自身:SELF,這個(gè)只針對字符數(shù)組起作用。如:

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

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

5、通配符:LIKE

?????LIKE 使用?表示一個(gè)字符,*表示多個(gè)字符,也可以與c、d 連用。如:

?????@"car.name LIKE '?he?'" //四個(gè)字符中,中間為he

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

6、正則表達(dá)式: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、邏輯運(yùn)算符: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,因此你可以有多個(gè)占位符,只要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ù)的同時(shí)不阻塞占用NSManagedObjectContext?,并可以隨時(shí)取消抓取行為,隨時(shí)跟蹤抓取數(shù)據(jù)的進(jìn)度。

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

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

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

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

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

舉個(gè)栗子:

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

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

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

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

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

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

}

????????Swift代碼很簡潔,并用了尾隨閉包語法,看不懂的朋友也不用著急,知道NSAsynchronousFetchRequest 大概的用法就行。之前提到過?NSAsynchronousFetchRequest?能在抓取數(shù)據(jù)的過程中跟蹤進(jìn)度,于是乎?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?就可以了!取消行為會(huì)沿著數(shù)的根節(jié)點(diǎn)蔓延到葉子。

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 新增臨時(shí)實(shí)體對象實(shí)例

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后要進(jìn)行保存,否則沒更新??

????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?中并保存,這會(huì)占用大量內(nèi)存,試想想在iPhone這樣的內(nèi)存有限的移動(dòng)設(shè)備上將是個(gè)災(zāi)難,數(shù)據(jù)有可能丟失。你可能會(huì)采取批處理的方式,即一小批一小批的更新NSManagedObject并保存到?NSManagedObjectContext?中,但這樣會(huì)花費(fèi)很多時(shí)間,用戶體驗(yàn)較差。

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

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

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

????????你大概猜到了,?NSBatchUpdateResult?對應(yīng)著前面的NSBatchUpdateRequest?,下面說說NSBatchUpdateRequest?。它有點(diǎn)像NSFetchRequest?:它允許你指定一個(gè)想要更新數(shù)據(jù)的實(shí)體;也可以指定一個(gè)affectedStores?,它存儲(chǔ)了一個(gè)接受更新請求的?NSPersistentStore?數(shù)組。(其實(shí)它是?NSPersistentStoreRequest?的屬性);它也有一個(gè)謂詞屬性來做更新的條件,它跟NSFetchRequest中的謂詞一樣強(qiáng)大和靈活,類似于SQL的where語句;它允許你指定想要更新的字段,通過?propertiesToUpdate?屬性來描述字段更新,它是一個(gè)字段,key為?NSPropertyDescription?或?qū)傩悦址瑅alue為?NSExpression?或常量。

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

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

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


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)建實(shí)體

- (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升級(jí)

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

8.1.1 支持場景

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

??????????? 為Entity簡單的添加一個(gè)屬性

??????????? 為Entity移除一個(gè)屬性

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

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

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

??????????? 增加一個(gè)新的relationship 或者刪除一個(gè)已經(jīng)存在的relationship

??????????? 重命名relationship

??????????? 改變r(jià)elationship to-one<-> to-many 等

? ? ? ? ? ? ?增加,刪除Entities

??????????? 增加新的 Parent 或者Child Entity

??????????? 從Hierarchy中移除Entities

????????輕量級(jí)遷移不支持合并Entity的層級(jí):比如在舊的Model中兩個(gè)已知的Entities沒有共享一個(gè)共同的Parent Entity,那么在新的Model中它們也不能夠共享一個(gè)共同的Parent Entity。


8.1.2 步驟

????1.升級(jí)數(shù)據(jù)庫模型:選中你的mydata.xcdatamodeld文件,選擇菜單editor->Add Model Version 比如取名:mydata2.xcdatamodel;

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

????3.修改新數(shù)據(jù)模型mydata2,在新的文件上添加,修改或刪除字段及表;

????4.在程序啟動(dòng)時(shí)添加如下代碼:

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模型版本升級(jí)和數(shù)據(jù)遷移(-)簡介

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


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

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


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

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


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

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


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

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


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

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


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

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


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

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


Core Data版本遷移經(jīng)驗(yàn)總結(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ù)同步時(shí)的操作

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)避免的十個(gè)錯(cuò)誤

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)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

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

  • rljs by sennchi Timeline of History Part One The Cognitiv...
    sennchi閱讀 7,438評論 0 10
  • 我從小時(shí)候到現(xiàn)在,一直都有一種隱蔽的愛好,就是看人吵架。 并不是說我喜歡在道德基礎(chǔ)上,去評判出一個(gè)孰對孰錯(cuò),我沒有...
    油爆苦瓜閱讀 253評論 0 0
  • 我認(rèn)為最難過的,除了痛苦就是離別。 離別是人人都經(jīng)歷過的,人與人或人與物之間一旦有了情,就變得難舍難分了。每當(dāng)離別...
    7db0debe5b84閱讀 336評論 0 1
  • 有句話說得好,聽再多的道理,還是沒能過好這一生。 為什么? 因?yàn)槟闶歉饍?yōu)躺著看的。 康哥分享一篇關(guān)于自律的文章,我...
    自律的哲仔媽媽閱讀 299評論 0 1
  • 【以終為始】20171010數(shù)里作業(yè)踐行D2 1.用樂高五和湊十的玩法。點(diǎn)數(shù)沒有問題。 2.自己主動(dòng)要求玩百數(shù)方格...
    王蘭_hope閱讀 488評論 0 0