11

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

我不知道 each 的确切类型NSValue,所以我仅限于使用NSValueand提供的接口NSData。我应该提一下,从NSValue不是指针类型。

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

我该怎么办?

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

4

3 回答 3

17

Martin Gordon 的答案越来越接近,但幸运的是您不必手动解析 objCType 字符串。有一个函数可以做到这一点:NSGetSizeAndAlignment。从中您可以获得从 getValue: 获得的值的大小/长度。这个问题引导我找到解决方案。

我最终为 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 数据之前添加了一个小标题,它允许我解码数据用于哪个属性以及数据部分中有多少字节。有了它,我可以将值恢复到远程属性。

于 2011-12-09T20:28:51.197 回答
4

由于NSValue采用NSCoding协议,您可以使用NSCoder子类,NSKeyedArchiver并且NSKeyedUnarchiver

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

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

如果NSKeyedArchiver并且NSKeyedUnarchiver不能使用(对于产生的数据大小问题),那么您必须自己找到包含值的大小:

- (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 类型编码

于 2011-12-09T15:12:52.660 回答
0

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

于 2011-12-09T15:10:10.073 回答