核心数据更改属性从Integer 16到Integer 32

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.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/

Privat,Pro Core Data for iOS Ch 8 p273


Mar*_*rra 9

打开NSMigratePersistentStoresAutomaticallyOption' and 'NSInferMappingModelAutomaticallyOption您的NSPersistentStore,然后使用更改创建模型的第二个版本.仅进行整数更改以保持迁移简单.这将允许安装升级的用户从损坏的模型迁移到更正的模型.

注意:必须是自动迁移; 使用映射模型的手动迁移将不起作用.