【问题标题】:NSKeyedArchiver: How to archive an NSValue containing a custom structNSKeyedArchiver:如何归档包含自定义结构的 NSValue
【发布时间】:2013-06-05 09:13:07
【问题描述】:

有什么方法可以使用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

仅仅是“这个归档器不能编码你的结构”这就是故事的结尾还是有可能得到与@987654327相同的行为@ 但对于自定义结构?

我可能会创建一个小的自定义对象,它包装一个 NSValue 并实现 NSCoding 来解决它,但我很好奇上面代码中显示的与 CGPoint 的矛盾,并想知道是否存在扩展 NSValue 以获得相同行为的方法。

【问题讨论】:

    标签: objective-c cocoa nskeyedarchiver nscoding nsvalue


    【解决方案1】:

    @encode 和匿名结构存在一些问题。尝试将结构定义更改为:

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

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

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

    【讨论】:

    • 感谢您的回答。替代结构定义没有解决错误。关于使用NSData 替代NSValue 来包装结构。您知道它们是否有任何可移植性考虑?归档数据最终可能会通过 iCloud / Game Center 在设备(OSX 和 iOS)之间移动。
    • 是的,不同字节序的处理器之间可能存在可移植性问题...
    【解决方案2】:

    以下示例说明了对自定义 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)];   
    

    虚数

    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 对象存在时释放数据的内存。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-04-28
      • 2012-01-11
      • 2018-10-01
      相关资源
      最近更新 更多