我有以下实现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
工作正常,但当我尝试从文件加载现有实例时,应用程序崩溃,因为它正在尝试解码int
和float
。< / p>
有没有一个很好的方法来处理这个?理想情况下,我可以加载现有的int
值并将其转换为float
,但我不介意只使用默认值而不会崩溃。
我考虑过在try-catch中包含适用的decode
行,但在我的实际用例中,大约有50个左右的属性正在编码/解码,所以不要对每个改变类型的人进行明确处理。
答案 0 :(得分:7)
当我自己处理这种情况时(我有几次),我使用 版本控制 。换句话说,将版本号与其余编码数据一起编码。
即使您从一开始就没有进行版本控制,这不是真正的问题,您现在可以从版本1
开始,只需将缺少version
值视为等同于版本{ {1}}。
如何?
在您要编码的对象中存储0
字段。在此字段中使用version
或int
可能是最佳选择。
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
}