NSKeyedArchiver:如何归档包含自定义结构的NSValue

时间:2013-06-05 09:13:08

标签: objective-c cocoa nskeyedarchiver nscoding nsvalue

有什么方法可以使用NSKeyedArchiver来编码包含自定义结构的NSValue?我有一个NSDictionary,其中包含NSValues中包含的结构,我想将其存档为更大的对象图的一部分但我收到以下错误:

-[NSKeyedArchiver encodeValueOfObjCType:at:]: this archiver cannot encode structs

但是,请考虑以下事项:

//Copy of the CGPoint declaration
struct MYPoint { 
    CGFloat x;
    CGFloat y;
};
typedef struct MYPoint MYPoint;

CGPoint point   = {1.0, 1.0};
MYPoint myPoint = {1.0, 1.0};

NSLog(@"CGPoint: %s", @encode(CGPoint)); //CGPoint: {CGPoint=ff}
NSLog(@"MYPoint: %s", @encode(MYPoint)); //MYPoint: {MYPoint=ff}

NSValue *CGPointValue = [NSValue valueWithBytes:&point objCType:@encode(CGPoint)];
NSData  *CGPointData  = [NSKeyedArchiver archivedDataWithRootObject:CGPointValue];
//NO ERROR    

NSValue *MYPointValue = [NSValue valueWithBytes:&myPoint objCType:@encode(MYPoint)];
NSData  *MYPointData  = [NSKeyedArchiver archivedDataWithRootObject:MYPointValue];
//ERROR: -[NSKeyedArchiver encodeValueOfObjCType:at:]: this archiver cannot encode structs

是否只是“此归档程序无法编码您的结构”,这就是故事的结尾,或者是否可能获得与{{{ 1}}但是对于自定义结构?

我可能只是创建一个包含CGPoint并实现NSValue的小型自定义对象来解决它,但我对上面代码中NSCoding所显示的矛盾感到好奇。并想知道是否有办法扩展NSValue以获得相同的行为。

2 个答案:

答案 0 :(得分:1)

有些问题与@encode和匿名结构有关。尝试将结构定义更改为:

typedef struct MYPoint { 
    CGFloat x;
    CGFloat y;
} MYPoint;

如果这不起作用,您可以使用NSData包装结构:

[NSData dataWithBytes:&myPoint length:sizeof(MYPoint)];

答案 1 :(得分:0)

以下示例说明了对自定义C结构的编码。

//assume ImaginaryNumber defined:
    typedef struct {
            float real;
            float imaginary; 
    } ImaginaryNumber;
            ImaginaryNumber miNumber; 
    miNumber.real = 1.1; 
    miNumber.imaginary = 1.41;   
    NSValue *miValue = [NSValue valueWithBytes: &miNumber withObjCType:@encode(ImaginaryNumber)];   

ImaginaryNumber

miNumber2;  
[miValue getValue:&miNumber2

];

您指定的类型必须是恒定长度。您不能在NSValue中存储C字符串,可变长度数组和结构以及其他不确定长度的数据类型 - 您应该为这些类型使用NSString或NSData对象。您可以在NSValue对象中存储指向可变长度项的指针。以下代码摘录错误地尝试将C字符串直接放入NSValue对象中:

/* INCORRECT! */
char *myCString = "This is a string.";
NSValue *theValue = [NSValue valueWithBytes:myCString withObjCType:@encode(char *)];

在此代码摘录中,myCString的内容被解释为指向char的指针,因此字符串中包含的前四个字节被视为指针(实际使用的字节数可能因硬件架构而异)。也就是说,序列“This”被解释为指针值,它不太可能是合法地址。存储此类数据项的正确方法是使用NSString对象(如果需要包含对象中的字符),或者传递其指针的地址,而不是指针本身:

/* Correct. */
char *myCString = "This is a string.";
NSValue *theValue = [NSValue valueWithBytes:&myCString withObjCType:@encode(char **)];

此处传递myCString的地址(& myCString),因此字符串的第一个字符的地址存储在theValue中。

重要说明:NSValue对象不会复制字符串的内容,而是复制指针本身。如果使用分配的数据项创建NSValue对象,则在NSValue对象存在时不要释放数据的内存。