Core Data Model Versioning and Data Migration Programming Guide

谷梁俊楚
2023-12-01

https://developer.apple.com/library/prerelease/ios/documentation/Cocoa/Conceptual/CoreDataVersioning/Articles/vmLightweightMigration.html

Lightweight Migration

If you just make simple changes to your model (such as adding a new attribute to an entity), Core Data can perform automatic data migration, referred to as lightweight migration. Lightweight migration is fundamentally the same as ordinary migration, except that instead of you providing a mapping model (as described in Mapping Overview), Core Data infers one from differences between the source and destination managed object models.

Lightweight migration is especially convenient during early stages of application development, when you may be changing your managed object model frequently, but you don’t want to have to keep regenerating test data. You can migrate existing data without having to create a custom mapping model for every model version used to create a store that would need to be migrated.

A further advantage of using lightweight migration—beyond the fact that you don’t need to create the mapping model yourself—is that if you use an inferred model and you use the SQLite store, then Core Data can perform the migration in situ (solely by issuing SQL statements). This can represent a significant performance benefit as Core Data doesn’t have to load any of your data. Because of this, you are encouraged to use inferred migration where possible, even if the mapping model you might create yourself would be trivial.

Core Data Must Be Able to Infer the Mapping

To perform automatic lightweight migration, Core Data needs to be able to find the source and destination managed object models itself at runtime. Core Data looks for models in the bundles returned by NSBundle’s allBundles and allFrameworks methods. If you store your models elsewhere, you must follow the steps described in Use a Migration Manager if Models Cannot Be Found Automatically . Core Data must then analyze the schema changes to persistent entities and properties and generate an inferred mapping model.

For Core Data to be able to generate an inferred mapping model, changes must fit an obvious migration pattern, for example:

  • Simple addition of a new attribute

  • Removal of an attribute

  • A non-optional attribute becoming optional

  • An optional attribute becoming non-optional, and defining a default value

  • Renaming an entity or property

If you rename an entity or property, you can set the renaming identifier in the destination model to the name of the corresponding property or entity in the source model. You set the renaming identifier in the managed object model using the Xcode Data Modeling tool’s property inspector (for either an entity or a property). For example, you can:

  • Rename a Car entity to Automobile

  • Rename a Car’s color attribute to paintColor

The renaming identifier creates a “canonical name,” so you should set the renaming identifier to the name of the property in the source model (unless that property already has a renaming identifier). This means you can rename a property in version 2 of a model then rename it again version 3, and the renaming will work correctly going from version 2 to version 3 or from version 1 to version 3.

In addition, Core Data supports:

  • Adding relationships and changing the type of relationship

    • You can add a new relationship or delete an existing relationship.

    • Renaming a relationship (by using a renaming identifier, just like an attribute)

    • Changing a relationship from a to-one to a to-many, or a non-ordered to-many to ordered (and visa-versa)

  • Changing the entity hierarchy

    • You can add, remove, rename entities

    • You can create a new parent or child entity and move properties up and down the entity hierarchy

    • You can move entities out of a hierarchy

      You cannot, however, merge entity hierarchies; if two existing entities do not share a common parent in the source, they cannot share a common parent in the destination

Request Automatic Migration Using an Options Dictionary

You request automatic lightweight migration using the options dictionary you pass in addPersistentStoreWithType:configuration:URL:options:error:, by setting values corresponding to both the NSMigratePersistentStoresAutomaticallyOption and the NSInferMappingModelAutomaticallyOption keys to YES:

NSError *error = nil;
NSURL *storeURL = <#The URL of a persistent store#>;
NSPersistentStoreCoordinator *psc = <#The coordinator#>;
NSDictionary *options = [NSDictionary dictionaryWithObjectsAndKeys:
[NSNumber numberWithBool:YES], NSMigratePersistentStoresAutomaticallyOption,
[NSNumber numberWithBool:YES], NSInferMappingModelAutomaticallyOption, nil];
 
BOOL success = [psc addPersistentStoreWithType:<#Store type#>
configuration:<#Configuration or nil#> URL:storeURL
options:options error:&error];
if (!success) {
// Handle the error.
}

If you want to determine in advance whether Core Data can infer the mapping between the source and destination models without actually doing the work of migration, you can use NSMappingModel’s inferredMappingModelForSourceModel:destinationModel:error: method. This returns the inferred model if Core Data is able to create it, otherwise nil.

Use a Migration Manager if Models Cannot Be Found Automatically

To perform automatic migration, Core Data has to be able to find the source and destination managed object models itself at runtime (see Core Data Must Be Able to Infer the Mapping). If you need to put your models in the locations not checked by automatic discovery, then you need to generate the inferred model and initiate the migration yourself using a migration manager (an instance of NSMigrationManager).

The following code sample illustrates how to generate an inferred model and initiate the migration using a migration manager. The code assumes that you have implemented two methods—sourceModel and destinationModel—that return the source and destination managed object models respectively.

- (BOOL)migrateStore:(NSURL *)storeURL toVersionTwoStore:(NSURL *)dstStoreURL error:(NSError **)outError {
 
// Try to get an inferred mapping model.
NSMappingModel *mappingModel =
[NSMappingModel inferredMappingModelForSourceModel:[self sourceModel]
destinationModel:[self destinationModel] error:outError];
 
// If Core Data cannot create an inferred mapping model, return NO.
if (!mappingModel) {
return NO;
}
 
// Create a migration manager to perform the migration.
NSMigrationManager *manager = [[NSMigrationManager alloc]
initWithSourceModel:[self sourceModel] destinationModel:[self destinationModel]];
 
BOOL success = [manager migrateStoreFromURL:storeURL type:NSSQLiteStoreType
options:nil withMappingModel:mappingModel toDestinationURL:dstStoreURL
destinationType:NSSQLiteStoreType destinationOptions:nil error:outError];
 
return success;
}

Note: Prior to OS X v10.7 and iOS 4, you need to use a store-specific migration manager to perform lightweight migration. You get the migration manager for a given persistent store type using migrationManagerClass, as illustrated in the following example.

- (BOOL)migrateStore:(NSURL *)storeURL toVersionTwoStore:(NSURL *)dstStoreURL error:(NSError **)outError {
 
// Try to get an inferred mapping model.
NSMappingModel *mappingModel =
[NSMappingModel inferredMappingModelForSourceModel:[self sourceModel]
destinationModel:[self destinationModel] error:outError];
 
// If Core Data cannot create an inferred mapping model, return NO.
if (!mappingModel) {
return NO;
}
 
// Get the migration manager class to perform the migration.
NSValue *classValue =
[[NSPersistentStoreCoordinator registeredStoreTypes] objectForKey:NSSQLiteStoreType];
Class sqliteStoreClass = (Class)[classValue pointerValue];
Class sqliteStoreMigrationManagerClass = [sqliteStoreClass migrationManagerClass];
 
NSMigrationManager *manager = [[sqliteStoreMigrationManagerClass alloc]
initWithSourceModel:[self sourceModel] destinationModel:[self destinationModel]];
 
BOOL success = [manager migrateStoreFromURL:storeURL type:NSSQLiteStoreType
options:nil withMappingModel:mappingModel toDestinationURL:dstStoreURL
destinationType:NSSQLiteStoreType destinationOptions:nil error:outError];
 
return success;
}

 类似资料:

相关阅读

相关文章

相关问答