如何将NSValue转换为NSData并返回?

时间:2011-12-09 15:01:29

标签: objective-c nsdata key-value-coding nscoding

A有一些NSValue(通过KVC获得valueForKey)我需要附加到NSData对象,以便使用Game Center通过网络发送它。显然,我还需要将NSValueNSData转换回更多KVC(setValue:forKey:)。

我不知道每个NSValue的确切类型,因此我仅限于使用NSValueNSData提供的界面。我应该提到NSValue永远不是指针类型。

我很惊讶没有[NSData dataWithValue:value],也没有类似[value dataWithEncoding:]或类似的东西。但也许我是盲目的,没有看到明显的选择。我想过使用getValue:将值复制到void*缓冲区,但除了使用objCType并将其与所有可能的类型进行比较之外,我应该如何确定缓冲区长度呢? / p>

我应该怎么做?

注意: NSKeyedArchiver是不可能的,因为它非常低效。 4字节浮点数归档为142字节NSData,8字节CGPoint在归档到NSData时使用260字节。将发送的数据量保持在最低限度对我正在做的事情至关重要。

3 个答案:

答案 0 :(得分:17)

Martin Gordon的答案越来越近了,但幸运的是你不必手动解析objCType字符串。有一个功能可以做到:NSGetSizeAndAlignment。从中你可以得到从getValue:获得的值的大小/长度。 This question引导我找到解决方案。

我最终为NSData创建了一个类别,允许我从NSNumber或NSValue对象创建NSData:

@interface NSData (GameKitCategory)
+(NSData*) dataWithValue:(NSValue*)value;
+(NSData*) dataWithNumber:(NSNumber*)number;
@end

@implementation NSData (GameKitCategory)
+(NSData*) dataWithValue:(NSValue*)value
{
    NSUInteger size;
    const char* encoding = [value objCType];
    NSGetSizeAndAlignment(encoding, &size, NULL);

    void* ptr = malloc(size);
    [value getValue:ptr];
    NSData* data = [NSData dataWithBytes:ptr length:size];
    free(ptr);

    return data;
}

+(NSData*) dataWithNumber:(NSNumber*)number
{
    return [NSData dataWithValue:(NSValue*)number];
}
@end

我还在此NSValue / NSNumber数据之前添加了一个小标题,允许我解码数据的属性以及数据部分中的字节数。有了它,我可以将值恢复到远程属性。

答案 1 :(得分:4)

由于NSValue采用NSCoding协议,您可以使用NSCoder子类,NSKeyedArchiverNSKeyedUnarchiver

- (NSData *)dataWithValue:(NSValue *)value {
  return [NSKeyedArchiver archivedDataWithRootObject:value];
}

- (NSValue *)valueWithData:(NSData *)data {
  return (NSValue *)[NSKeyedUnarchiver unarchiveObjectWithData:data];
}

如果无法使用NSKeyedArchiverNSKeyedUnarchiver(针对结果数据大小问题),那么您必须自己查找所包含值的大小:

- (NSData *)dataWithValue:(NSValue *)value {
  // Can use NSGetSizeAndAlignment() instead. See LearnCocos2D's answer.
  if (type[0] == '{') {
    // Use the various NSValue struct value methods to detect the type.
  } else if (type[0] == 'c') {
    size = sizeof(char);
  } else if (type[0] == 'i') {
    size = sizeof(int);     
  } // etc for all/most of the values in the table linked below [1];

  void *bytes = malloc(size);
  [value getValue:bytes];
  return [NSData dataWithBytes:bytes length:size];
}

[1]:Objective-C Type Encodings

答案 2 :(得分:0)

您可以使用NSCoder(如NSKeyedArchiver)归档数据,然后发送生成的NSData。另一方面,你可以解开它。如果您在NSValue中包装结构和其他非归档内容,可能会有一些警告(例如http://www.cocoabuilder.com/archive/cocoa/82344-nskeyedarchiver-and-nsvalue.html)。