核心数据将属性从整数 16 更改为整数 32

2023-12-06

我有一个非常严重的问题。该应用程序已上线,但不幸的是它在 iOS 5 上失败,我需要发布更新。

问题是少数实体的 ID 列是整数 16,但我需要更改为整数 32。

这显然是我的错误,该模型是很久以前创建的,并且只是被重用。令我惊讶的是(现在)在 iOS 4 上,Core Data 中的 Integer 16 可以轻松地将数字保持为 500 000 大(bug?),但它现在不能像那样工作了 - 它给了我无效的数字。

应用程序已上线,是否成功,核心数据还用于保存用户分数、成就等,我不想删除这些内容,迫使他们重新安装应用程序。将不同实体中的大约 10 个属性从整数 16 更改为整数 32 的最佳方法是什么?

我当然知道这些属性的名称和实体。

如果我只是更改 xcdatamodeld 文件中这些属性的“类型”列,对于新用户来说它将起作用,但对于现有用户(在其文档文件夹中已有 sqlite 文件)又如何呢?我相信我需要以某种方式更改持久存储协调器。

另外,您对性能有何看法,大约有 10 个属性从 16 个更改为 32 个,但 Core Data 通常会包含超过 100 000 个对象。

Regards


背景
以前版本的应用程序在 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 核心数据

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

核心数据将属性从整数 16 更改为整数 32 的相关文章

随机推荐