背景
以前版本的应用程序在 Core Data 中将属性设置为 16 位。
这个值太小,无法容纳大于约 32768 的大值。
int 16 使用 1 位来表示符号,因此最大值 = 2^15 = 32768
在 iOS 5 中,这些值溢出为负数。
34318 变成-31218
36745 变成-28791
要修复这些负值,请添加 2^16 = 65536
请注意,此解决方案仅在原始值小于 65536 时才有效。
添加新模型
在文件导航器中,选择 MyApp.xcdatamodeld
选择菜单编辑器/添加模型版本
版本名称:建议使用“MyApp 2”,但您可以更改,例如到我的应用程序版本2
基于模型:MyApp
在新的 MyAppVersion2.xcdatamodel 中,将属性类型从整数 16 更改为整数 64。
在文件导航器中,选择目录 MyApp.xcdatamodeld
打开右窗格检查器,版本化核心数据模型当前从 MyApp 更改为 MyAppVersion2。
在左窗格文件导航器中,绿色复选标记从 MyApp.xcdatamodel 移至 MyAppVersion2.xcdatamodel。
在 MyAppAppDelegate ManagedObjectModel 中,不要更改 @"MyApp" 中的资源名称
在 Xcode 中选择文件夹 ModelClasses。
文件/添加核心数据映射模型。
选择源数据模型 MyApp.xcdatamodel
选择目标数据模型 MyAppVersion2.xcdatamodel
另存为 MyAppToMyAppVersion2.xcmappingmodel
添加到目标 MyApp。
在 MyApp AppDelegate persistenceStore Coordinator 中开启 Core Data 手动迁移
// Returns the persistent store coordinator for the application.
// If the coordinator doesn't already exist, it is created
// and the application's store added to it.
- (NSPersistentStoreCoordinator *)persistentStoreCoordinator {
if (persistentStoreCoordinator_ != nil) {
return persistentStoreCoordinator_;
}
NSURL *storeURL = [NSURL fileURLWithPath: [[self applicationDocumentsDirectory]
stringByAppendingPathComponent: @"MyApp.sqlite"]];
NSError *error = nil;
persistentStoreCoordinator_ = [[NSPersistentStoreCoordinator alloc]
initWithManagedObjectModel:[self managedObjectModel]];
// Set Core Data migration options
// For automatic lightweight migration set NSInferMappingModelAutomaticallyOption to YES
// For manual migration using a mapping model set NSInferMappingModelAutomaticallyOption to NO
NSDictionary *optionsDictionary = [NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithBool:YES],
NSMigratePersistentStoresAutomaticallyOption,
[NSNumber numberWithBool:NO],
NSInferMappingModelAutomaticallyOption,
nil];
if (![persistentStoreCoordinator_ addPersistentStoreWithType:NSSQLiteStoreType
configuration:nil
URL:storeURL
options:optionsDictionary
error:&error])
{
// handle the error
NSString *message = [[NSString alloc]
initWithFormat:@"%@, %@", error, [error userInfo]];
UIAlertViewAutoDismiss *alertView = [[UIAlertViewAutoDismiss alloc]
initWithTitle:NSLocalizedString(@"Sorry, Persistent Store Error. Please Quit.", @"")
message:message
delegate: nil
cancelButtonTitle:NSLocalizedString(@"OK", @"")
otherButtonTitles:nil];
[message release];
[alertView show];
[alertView release];
}
return persistentStoreCoordinator_;
}
添加迁移策略
我的应用程序到我的应用程序版本2迁移策略
以下示例将一个实体“Environment”与整数属性“FeedID”和字符串属性“title”进行转换。
- (BOOL)createDestinationInstancesForSourceInstance:(NSManagedObject *)aSource
entityMapping:(NSEntityMapping *)mapping
manager:(NSMigrationManager *)migrationManager
error:(NSError **)error {
NSEntityDescription *aSourceEntityDescription = [aSource entity];
NSString *aSourceName = [aSourceEntityDescription valueForKey:@"name"];
NSManagedObjectContext *destinationMOC = [migrationManager destinationContext];
NSManagedObject *destEnvironment;
NSString *destEntityName = [mapping destinationEntityName];
if ([aSourceName isEqualToString:kEnvironment])
{
destEnvironment = [NSEntityDescription
insertNewObjectForEntityForName:destEntityName
inManagedObjectContext:destinationMOC];
// attribute feedID
NSNumber *sourceFeedID = [aSource valueForKey:kFeedID];
if (!sourceFeedID)
{
// Defensive programming.
// In the source model version, feedID was required to have a value
// so excecution should never get here.
[destEnvironment setValue:[NSNumber numberWithInteger:0] forKey:kFeedID];
}
else
{
NSInteger sourceFeedIDInteger = [sourceFeedID intValue];
if (sourceFeedIDInteger < 0)
{
// To correct previous negative feedIDs, add 2^16 = 65536
NSInteger kInt16RolloverOffset = 65536;
NSInteger destFeedIDInteger = (sourceFeedIDInteger + kInt16RolloverOffset);
NSNumber *destFeedID = [NSNumber numberWithInteger:destFeedIDInteger];
[destEnvironment setValue:destFeedID forKey:kFeedID];
} else
{
// attribute feedID previous value is not negative so use it as is
[destEnvironment setValue:sourceFeedID forKey:kFeedID];
}
}
// attribute title (don't change this attribute)
NSString *sourceTitle = [aSource valueForKey:kTitle];
if (!sourceTitle)
{
// no previous value, set blank
[destEnvironment setValue:@"" forKey:kTitle];
} else
{
[destEnvironment setValue:sourceTitle forKey:kTitle];
}
[migrationManager associateSourceInstance:aSource
withDestinationInstance:destEnvironment
forEntityMapping:mapping];
return YES;
} else
{
// don't remap any other entities
return NO;
}
}
在文件导航器中选择 MyAppToMyAppVersion2.xcmappingmodel
在窗口中,显示右侧实用程序窗格。
在窗口中,选择实体映射环境到环境
在右侧实体映射中,选择自定义策略,输入 MyAppToMyAppVersion2MigrationPolicy。
保存存档。
参考:
Zarra,《核心数据》第 5 章,第 87 页http://pragprog.com/book/mzcd/core-data
http://www.timisted.net/blog/archive/core-data-migration/
http://www.cocoabuilder.com/archive/cocoa/286529-core-data-versioning-non-trivial-value-expressions.html
http://www.seattle-ipa.org/2011/09/11/coredata-and-integer-width-in-ios-5/
适用于 iOS Ch 8 p273 的 Private、Pro 核心数据