使用NSCoding时如何处理字段类型更改

时间:2015-04-22 21:41:26

标签: objective-c nscoding

我有以下实现NSCoding的类,我创建了几个实例并将它们保存到文件中。

@interface BiscuitTin ()
@property NSString *biscuitType;
@property int numBiscuits;
@end

@implementation BiscuitTin

- (id)initWithCoder:(NSCoder *)coder {
    self = [super init];
    if (self) {
        self.biscuitType = [coder decodeObjectForKey:@"biscuitType"];
        self.numBiscuits = [coder decodeIntForKey:@"numBiscuits"];
    }
    return self;
}

- (void)encodeWithCoder:(NSCoder *coder) {
    [coder encodeObject:self.biscuitType forKey:@"biscuitType"];
    [coder encodeInt:self.numBiscuits forKey:@"numBiscuits"];
}

@end

我现在决定将numBiscuits表示为float(因为可能有部分吃过的饼干)。更新属性类型和encodeWithCoder工作正常,但当我尝试从文件加载现有实例时,应用程序崩溃,因为它正在尝试解码intfloat。< / p>

有没有一个很好的方法来处理这个?理想情况下,我可以加载现有的int值并将其转换为float,但我不介意只使用默认值而不会崩溃。

我考虑过在try-catch中包含适​​用的decode行,但在我的实际用例中,大约有50个左右的属性正在编码/解码,所以不要对每个改变类型的人进行明确处理。

2 个答案:

答案 0 :(得分:7)

当我自己处理这种情况时(我有几次),我使用 版本控制 。换句话说,将版本号与其余编码数据一起编码。

即使您从一开始就没有进行版本控制,这不是真正的问题,您现在可以从版本1开始,只需将缺少version值视为等同于版本{ {1}}。

如何?

在您要编码的对象中存储0字段。在此字段中使用versionint可能是最佳选择。

NSInteger

每当您添加不向后兼容的新功能时,请递增#define CURRENT_VERSION 1 #define MIN_VERSION_WITH_FEATURE_X 1 @implementation BiscuitTin - (id)initWithCoder:(NSCoder *)coder { self = [super init]; if (self) { ... int vers = [coder decodeIntForKey:@"version"]; if (vers >= MIN_VERSION_WITH_FEATURE_X) { // handle feature X. In your case, decoding a `float` } else { // handle prior version. // In your case, decoding an `int` and converting to `float` } } return self; } - (void)encodeWithCoder:(NSCoder *coder) { ... [coder encodeInt:CURRENT_VERSION forKey:@"version"]; } @end ,使用新的CURRENT_VERSION整数值添加新的MIN_VERSION_WITH_FEATURE_Y常量,并在{CURRENT_VERSION中添加另一个分支{1}}中的{1}}语句。

这有点乱,但我想这是向后兼容的成本。 (从好的方面来说,我认为这种技术是相当自我记录的,因此当你几个月或几年后返回它时很容易使用。)

答案 1 :(得分:4)

在类似的情况下,我更新了代码,以便以新名称保存更改的属性。然后initWithCoder:代码查找新名称。如果没有,它会查找旧名称下的旧值。

因此,代码的第2版(属性更改为float)将类似于以下内容:

@interface BiscuitTin ()
@property NSString *biscuitType;
@property float numBiscuits;
@end

- (id)initWithCoder:(NSCoder *)coder {
    self = [super init];
    if (self) {
        self.biscuitType = [coder decodeObjectForKey:@"biscuitType"];
        if ([coder containsValueForKey:@"numBiscuits2"]) {
            // Process a version 2 archive
            self.numBiscuits = [coder decodeFloatForKey:@"numBiscuits2"];
        } else if ([coder containsValueForKey:@"numBiscuits"]) {
            // Process a version 1 archive
            int oldIntVal = [coder decodeIntForKey:@"numBiscuits"];
            self.numBiscuits = oldIntVal;
        }
    }
    return self;
}

- (void)encodeWithCoder:(NSCoder *coder) {
    [coder encodeObject:self.biscuitType forKey:@"biscuitType"];
    // [coder encodeInt:self.numBiscuits forKey:@"numBiscuits"]; // obsolete version
    [coder encodeFloat:self.numBiscuits forKey:@"numBiscuits2"]; // new key name
}