Core Data编程指南 1-3:初始化Core Data协议栈

Core Data协议栈是指应用程序和外部存储设备的交互接口,主要在Core Data初始化过程中进行配置。它封装了大部分和外部存储设备间交互的操作流程,应用程序只需要专注于事务逻辑本身。Core Data协议栈主要由四个对象组成: 数据容器对象NSManagedObjectContext)、 外部存储器管理对象(NSPersistentStoreCoordinator)、 数据层模型对象(NSManagedObjectModel)和外部存储器容器 (NSPersistentContainer)。

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

Core Data协议栈必须在应用程序访问任何数据层数据之前初始化,Core Data会在初始化过程中准备好应用程序需要访问的数据。下面是一个Core Data协议栈的初始化示例。

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

表 3-1 Core Data初始化代码示例

Listing 3-1 Example Core Data stack creation

OBJECTIVE-C
-----------

@interface MyDataController : NSObject
@property (strong, nonatomic, readonly) NSPersistentContainer *persistentContainer;

- (id)initWithCompletionBlock:(CallbackBlock)callback;
@end

@implementation MyDataController

- (id)init
{
    self = [super init];
    if (!self) return nil;

    self.persistentContainer = [[NSPersistentContainer alloc] initWithName:@"DataModel"];
    [self.persistentContainer loadPersistentStoresWithCompletionHandler:
        ^(NSPersistentStoreDescription *description, NSError *error) {
            if (error != nil) {
                NSLog(@"Failed to load Core Data stack: %@", error);
                abort();
            }
            callback();
        }];

    return self;
}
SWIFT
-----

import UIKit
import CoreData

class DataController: NSObject {
    var managedObjectContext: NSManagedObjectContext
    init(completionClosure: @escaping () -> ()) {
        persistentContainer = NSPersistentContainer(name: "DataModel")
        persistentContainer.loadPersistentStores() { 
            (description, error) in
            if let error = error {
                fatalError("Failed to load Core Data stack: \(error)")
            }
            completionClosure()
        }
    }
}

这段代码演示了如何初始化外部存储控制器(NSPersistentContainer)。NSPersistentContainer可以由缺省构造函数(init)进行初始化。在构造函数内部,initializeCoreData方法真正创建了Core Data协议栈。

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

NSPersistentContainer

从 iOS 10 和 macOS 10.12开始, NSPersistentContainer对象负责创建Core Data协议栈,并提供访问 NSManagedObjectContext 对象的句柄和一系列辅助方法,简化了Core Data协议栈初始化流程。

Starting in iOS 10 and macOS 10.12, the NSPersistentContainer handles the creation of the Core Data stack and offers access to the NSManagedObjectContext as well as a number of convenience methods. Prior to iOS 10 and macOS 10.12, the creation of the Core Data stack was more involved.

OBJECTIVE-C
-----------

- (id)initWithCompletionBlock:(CallbackBlock)callback;
{
    self = [super init];
    if (!self) return nil;

    //This resource is the same name as your xcdatamodeld contained in your project
    NSURL *modelURL = [[NSBundle mainBundle] URLForResource:@"Workspace" withExtension:@"momd"];
    NSAssert(modelURL, @"Failed to locate momd bundle in application");

    // 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.
    NSManagedObjectModel *mom = [[NSManagedObjectModel alloc] initWithContentsOfURL: modelURL];
    NSAssert(mom, @"Failed to initialize mom from URL: %@", modelURL);

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

    NSManagedObjectContext *moc = [[NSManagedObjectContext alloc] initWithConcurrencyType: NSMainQueueConcurrencyType];
    [moc setPersistentStoreCoordinator:coordinator];
    [self setManagedObjectContext:moc];
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{
        NSPersistentStoreCoordinator *psc = [[self managedObjectContext] persistentStoreCoordinator];
        NSFileManager *fileManager = [NSFileManager defaultManager];
        NSURL *documentsURL = [[fileManager URLsForDirectory:NSDocumentationDirectory inDomains:NSUserDomainMask] lastObject];

        // The directory the application uses to store the Core Data store file. This code uses a file named "DataModel.sqlite" in the application's documents directory.
        NSURL *storeURL = [documentsURL URLByAppendingPathComponent:@"DataModel.sqlite"];

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

        if (!store) {
            NSLog(@"Failed to initalize persistent store: %@\n%@", [error localizedDescription], [error userInfo]);
            abort();
            //A more user facing error message may be appropriate here rather than just a console log and an abort
        }

        if (!callback) {
            //If there is no callback block we can safely return
            return;
        }

        //The callback block is expected to complete the User Interface and therefore should be presented back on the main queue so that the user interface does not need to be concerned with which queue this call is coming from.
        dispatch_sync(dispatch_get_main_queue(), ^{
            callback();
        });

    });
    return self;
}
SWIFT
-----
init(completionClosure: @escaping () -> ()) {
    //This resource is the same name as your xcdatamodeld contained in your project
    guard let modelURL = Bundle.main.url(forResource: "DataModel", withExtension:"momd") else {
        fatalError("Error loading model from bundle")
    }

    // The managed object model for the application. It is a fatal error for the application not to be able to find and load its model.
    guard let mom = NSManagedObjectModel(contentsOf: modelURL) else {
        fatalError("Error initializing mom from: \(modelURL)")
    }

    let psc = NSPersistentStoreCoordinator(managedObjectModel: mom)

    managedObjectContext = NSManagedObjectContext(concurrencyType: NSManagedObjectContextConcurrencyType.mainQueueConcurrencyType)
    managedObjectContext.persistentStoreCoordinator = psc

    let queue = DispatchQueue.global(qos: DispatchQoS.QoSClass.background)
    queue.async {
        guard let docURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).last else {
            fatalError("Unable to resolve document directory")
        }
        let storeURL = docURL.appendingPathComponent("DataModel.sqlite")
        do {
            try psc.addPersistentStore(ofType: NSSQLiteStoreType, configurationName: nil, at: storeURL, options: nil)
            //The callback block is expected to complete the User Interface and therefore should be presented back on the main queue so that the user interface does not need to be concerned with which queue this call is coming from.
            DispatchQueue.main.sync(execute: completionClosure)
        } catch {
            fatalError("Error migrating store: \(error)")
        }
    }
}

NSPersistentContainer也提供了一系列实用的多线程访问方法。

In addition to simplifying the construction of the Core Data stack, the NSPersistentContainer
also has a number of convenience methods that assist the developer when working with multithreaded applications.

NSManagedObjectModel

NSManagedObjectModel对象定义了Core Data协议栈将要访问的数据结构,在Core Data协议栈初始化过程中,它是第一个被加载的。在上面的示例中,我们用一个指向主程序包中特定文件(DataModel.momd)的NSURL 对象,创建了 NSManagedObjectModel对象和接下来的NSPersistentStoreCoordinator对象。

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

NSPersistentStoreCoordinator

NSPersistentStoreCoordinator对象是Core Data协议栈的中枢,负责和外部存储容器(NSPersistentStore)间同步数据对象内容,实例化数据对象。外部存储器可以是硬盘,也可以是内存中的其他数据库。甚至某些特殊场景下,NSPersistentStoreCoordinator需要和几个不同的外部存储器同步。

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

NSPersistentStoreCoordinator对象根据NSManagedObjectModel对象定义的数据结构在数据容器(NSManagedObjectContext)和外部存储器(NSPersistentStore)之间同步数据。。NSPersistentStoreCoordinator对象也会进行数据校验,保证流通的数据符合NSManagedObjectModel定义的规范。

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

NSPersistentStore 加入NSPersistentStoreCoordinator的方法上应该异步调用,在某些情况下,这个方法会阻塞调用线程(例如集成了iCloudMigrations),异步调用可以防止它阻塞用户界面线程。

The call to add the NSPersistentStore to the NSPersistentStoreCoordinator is performed asynchronously. A few situations can cause this call to block the calling thread (for example, integration with iCloud and Migrations). Therefore, it is better to execute this call asynchronously to avoid blocking the user interface queue.

NSManagedObjectContext

在初始化结束后,应用程序更多的是和数据容器对象(NSManagedObjectContext)打交道。可以将数据容器想象成一个便笺,从外部存储器中读取一个数据对象相当于在便笺上画上这个对象(或者一组对象)的形状,我们将这个“形状”叫做对象影像(object graph)。你可以在便笺上随意修改这些对象,并且只有保存之后,才会更新外部存储器中的相应内容。

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

所有的数据对象都必须注册到数据容器中去,只有数据容器才能将数据对象映射进内存,创建对象影像。数据容器会记录所有对字段和关系的修改历史,并且提供undo和redo功能。甚至上下文对象还会进行自动校验对关系的修改,并保证所有对象间关系的一致性。

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

当我们需要将对对象影像的修改过程保存到外部存储器中时,数据容器会保证数据处在一个稳定的状态(没有其他现成在访问,数据是完整的),然后才在外部存储器中添加或者删除对应记录。

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

Core Data通过数据容器对象封装了数据对象的读取、保存、校验等功能。如果没有Core Data,我们就需要自己编写函数去实现这些功能,甚至需要自己和undo管理器管理器打交道去实现undo功能。

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

你可能感兴趣的:(Core Data编程指南 1-3:初始化Core Data协议栈)