Chr*_*ski 3 xcode core-data core-data-migration
我有一个非常严重的问题.该应用程序是实时的,但不幸的是它在iOS 5上失败了,我需要发布更新.
事情是少数实体的ID列在Integer 16中,但我需要更改为Integer 32.
这显然是我的错,这个模型是很久以前创建的,它只是被重用了.令我惊讶的是(现在)在iOS 4上,核心数据中的整数16可以很容易地保持数字大到500 000(错误?),但它现在不能正常工作 - 它给了我无效的数字.
应用程序是实时的,它是成功的,Core Data也用于保持用户的分数,成就等,我不想删除,迫使他们重新安装应用程序.简单地将不同实体中的十个属性从Integer 16更改为Integer 32的最佳方法是什么?
当然,我知道这些属性的名称和实体.
如果我只是更改xcdatamodeld文件中这些属性的Type列,那么对于新用户,它将起作用,但现有用户的文档文件夹中已经有sqlite文件.我相信我需要以某种方式更改持久性商店协调员.
而且你对性能有什么看法,有大约10个属性将新闻从16改为32,但Core Data在通常情况下有超过10万个对象.
问候
bee*_*ore 12
背景
应用程序的先前版本在Core Data中将属性设置为16位.这太小而不能容纳大于约32768的大值.int
16使用1位来表示符号,因此最大值= 2 ^ 15 = 32768
在iOS 5中,这些值溢出为负数.
34318成为-31218
36745成为-28791
要修复这些负值,请添加2 ^ 16 = 65536
注意此解决方案仅在原始值小于65536时有效.
添加新模型
在文件导航器中,选择MyApp.xcdatamodeld
选择菜单编辑器/添加模型版本
版本名称:建议"MyApp 2",但您可以
根据型号更改为MyAppVersion2 :MyApp
在新的MyAppVersion2.xcdatamodel中,将属性类型从整数16更改为整数64.
在文件导航器中,选择目录MyApp.xcdatamodeld
打开右窗格检查器,Versioned Core Data Model当前从MyApp更改为MyAppVersion2.在左窗格文件导航器中,绿色复选标记从MyApp.xcdatamodel移动到MyAppVersion2.xcdatamodel.
在MyAppAppDelegate中,managedObjectModel不会更改@"MyApp"中的资源名称
在Xcode中选择文件夹ModelClasses.
文件/添加核心数据映射模型.
选择源数据模型MyApp.xcdatamodel
选择目标数据模型MyAppVersion2.xcdatamodel
另存为MyAppToMyAppVersion2.xcmappingmodel
添加到目标MyApp.
在MyAppAppDelegate中,persistentStoreCoordinator打开CoreData手动迁移
// 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_;
}
Run Code Online (Sandbox Code Playgroud)
添加迁移策略
MyAppToMyAppVersion2MigrationPolicy
以下示例使用整数属性"FeedID"和字符串属性"title"转换一个实体"Environment".
- (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;
}
}
Run Code Online (Sandbox Code Playgroud)
在文件导航器中,选择MyAppToMyAppVersion2.xcmappingmodel
在窗口中,显示右侧实用程序窗格.
在窗口中,选择Entity Mappings EnvironmentToEnvironment
在右侧Entity Mapping中,选择Custom Policy,输入MyAppToMyAppVersion2MigrationPolicy.
保存存档.
参考文献:
Zarra,核心数据第5章第87页 http://pragprog.com/book/mzcd/core-data
http://www.informit.com/articles/article.aspx?p=1178181&seqNum=7
http://www.timisted.net/blog/archive/core-data-migration/
http://www.seattle-ipa.org/2011/09/11/coredata-and-integer-width-in-ios-5/
Privat,Pro Core Data for iOS Ch 8 p273
打开NSMigratePersistentStoresAutomaticallyOption' and 'NSInferMappingModelAutomaticallyOption您的NSPersistentStore,然后使用更改创建模型的第二个版本.仅进行整数更改以保持迁移简单.这将允许安装升级的用户从损坏的模型迁移到更正的模型.
注意:这必须是自动迁移; 使用映射模型的手动迁移将不起作用.