我遇到了一个非常严重的问题。该应用程序是实时的,但不幸的是它在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万个对象。
此致
答案 0 :(得分:12)
<强>背景强>
以前版本的app set属性为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_;
}
添加迁移政策
MyAppToMyAppVersion2MigrationPolicy
以下示例将一个实体“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
在窗口中,显示右侧实用程序窗格
在窗口中,选择Entity Mappings EnvironmentToEnvironment
在右侧实体映射中,选择自定义策略,输入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,适用于iOS的Pro Core数据第8章第273页
答案 1 :(得分:9)
启用NSMigratePersistentStoresAutomaticallyOption' and 'NSInferMappingModelAutomaticallyOption
中的NSPersistentStore
,然后使用更改创建模型的第二个版本。仅进行整数更改以保持迁移简单。这将允许安装升级的用户从损坏的模型迁移到更正的模型。
注意:此必须是自动迁移;使用映射模型进行手动迁移将无法正常工作。
答案 2 :(得分:0)
只是想通过一个小小的补充确认Marcus S. Zarra的回答。它在某种程度上对我们有益。我们在模型中犯了完全相同的错误。但它有一个问题。在自动迁移期间,超过似乎为2 ^ 24的值将转换为16位值,但保存为32位但值不正确。
例如: 17 479 261变为18 851
(17 479 261 mod(2 ^ 16)) - (2 ^ 16)= -18 851
我们从手机下载了数据库并查看了数据库,并在数据库中更改了数字。
我们还没有解决这个问题。