NSKeyedArchiver 因 CLLocationCoordinate2D 结构而失败。为啥?

Posted

技术标签:

【中文标题】NSKeyedArchiver 因 CLLocationCoordinate2D 结构而失败。为啥?【英文标题】:NSKeyedArchiver fails with CLLocationCoordinate2D structs. Why?NSKeyedArchiver 因 CLLocationCoordinate2D 结构而失败。为什么? 【发布时间】:2012-08-30 18:05:31 【问题描述】:

我不明白为什么我可以存档 CGPoint 结构但不能存档 CLLocationCoordinate2D 结构。归档器有什么区别?

平台是 ios。我在模拟器中运行,还没有在设备上尝试过。

// why does this work:
NSMutableArray *points = [[[NSMutableArray alloc] init] autorelease];
CGPoint p = CGPointMake(10, 11);
[points addObject:[NSValue valueWithBytes: &p objCType: @encode(CGPoint)]];
[NSKeyedArchiver archiveRootObject:points toFile: @"/Volumes/Macintosh HD 2/points.bin" ];

// and this doesnt work:
NSMutableArray *coords = [[[NSMutableArray alloc] init] autorelease];
CLLocationCoordinate2D c = CLLocationCoordinate2DMake(121, 41);
[coords addObject:[NSValue valueWithBytes: &c objCType: @encode(CLLocationCoordinate2D)]];
[NSKeyedArchiver archiveRootObject:coords toFile: @"/Volumes/Macintosh HD 2/coords.bin" ];

我在 2 日遇到了崩溃 archiveRootObject,这条消息被打印到控制台:

*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '*** -[NSKeyedArchiver encodeValueOfObjCType:at:]: this archiver cannot encode structs'

【问题讨论】:

【参考方案1】:

要在错误修复之前绕过此限制,只需分解坐标并重建即可:

- (void)encodeWithCoder:(NSCoder *)coder

    NSNumber *latitude = [NSNumber numberWithDouble:self.coordinate.latitude];
    NSNumber *longitude = [NSNumber numberWithDouble:self.coordinate.longitude];
    [coder encodeObject:latitude forKey:@"latitude"];
    [coder encodeObject:longitude forKey:@"longitude"];
    ...

- (id)initWithCoder:(NSCoder *)decoder

    CLLocationDegrees latitude = (CLLocationDegrees)[(NSNumber*)[decoder decodeObjectForKey:@"latitude"] doubleValue];
    CLLocationDegrees longitude = (CLLocationDegrees)[(NSNumber*)[decoder decodeObjectForKey:@"longitude"] doubleValue];
    CLLocationCoordinate2D coordinate = (CLLocationCoordinate2D)  latitude, longitude ;
    ...

【讨论】:

【参考方案2】:

好的,汤姆,你准备好迎接极客了吗?在这个年轻的whippersnappers世界里,我是一个“老”的人。但是,我记得一些关于 C 的事情,而且我只是一个内心的极客。

不管怎样,这之间还是有细微差别的:

typedef struct  double d1, d2;  Foo1;

还有这个:

typedef struct Foo2  double d1, d2;  Foo2;

第一个是匿名结构的类型别名。第二个是struct Foo2 的类型别名。

现在,@encode 的文档说明如下:

typedef struct example 
    id   anObject;
    char *aString;
    int  anInt;
 Example;

对于@encode(example)@encode(Example) 将产生example=@*i。因此,这意味着 @encode 正在使用实际的结构标记。对于为匿名结构创建别名的 typedef,看起来 @encode 总是返回 ?'

看看这个:

NSLog(@"Foo1: %s", @encode(Foo1));
NSLog(@"Foo2: %s", @encode(Foo2));

不管怎样,你能猜出 CLLocationCoordinate2D 是如何定义的吗?是的。你猜对了。

typedef struct 
CLLocationDegrees latitude;
CLLocationDegrees longitude;
 CLLocationCoordinate2D;

我认为您应该就此提交错误报告。要么@encode 被破坏,因为它没有对匿名结构使用别名类型定义,要么 CLLocationCoordinate2D 需要完全类型化,因此它不是匿名结构。

【讨论】:

谢谢;这就是完美的解释!我会在我的雷达报告中引用它:) 现在定义为:struct CLLocationCoordinate2D CLLocationDegrees latitude; CLLocationDegrees 经度; ; typedef struct CLLocationCoordinate2D CLLocationCoordinate2D;所以我猜@encode 坏了。【参考方案3】:

这是因为@encode 阻塞了 CLLocationCoordinate2D

NSLog(@"coords %@; type: %s", coords, @encode(CLLocationCoordinate2D)); 产生coords ( "<00000000 00405e40 00000000 00804440>" ); type: ?=dd

【讨论】:

好的,这很有趣。所以@encode 得到它是一个有两个双精度的结构,但错过了类型名称。怎么会?真的,为什么类型名对编码结构很重要? 我认为编码会生成一个字符串,该字符串用于执行[[NSClassFromString(@encode(name)) alloc] init] 之类的操作或一些类似的黑魔法,以找到生成CLLocationCoordNSPoint 或诸如此类的函数。

以上是关于NSKeyedArchiver 因 CLLocationCoordinate2D 结构而失败。为啥?的主要内容,如果未能解决你的问题,请参考以下文章

NSKeyedArchiver 类 格式

在 NSKeyedArchiver 中查找不同的实例

导出/导入 NSKeyedArchiver 对象文件

从 NSKeyedArchiver 中提取 NSString

如何将 UIDocumentPickerViewController 导入限制为 .archive 文件? (NSKeyedArchiver)

NSArchiver 与 NSKeyedArchiver 性能对比