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

发布于 2024-12-20 13:16:08 字数 774 浏览 1 评论 0原文

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

我不知道每个 NSValue 的确切类型,因此我仅限于使用 NSValueNSData 提供的接口。我应该提到 NSValue 从来都不是指针类型。

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

我该怎么办?

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

A have a number of NSValue (obtained via KVC valueForKey) that I need to append to an NSData object in order to send it over the network using Game Center. Obviously I will also need to convert the NSValue back from NSData for more KVC (setValue:forKey:).

I don't know the exact type of each NSValue, so I'm limited to using the interfaces provided by NSValue and NSData. I should mention that the NSValue are never pointer types.

I'm surprised that there's no [NSData dataWithValue:value] and neither something like [value dataWithEncoding:] or similar. But maybe I'm blind and not seeing the obvious choice. I thought about using getValue: to copy the value into a void* buffer, but how am I supposed to determine the buffer length other than by using objCType and comparing that with all possible types?

How should I go about this?

NOTE:
NSKeyedArchiver is out of the question because it is terribly inefficient. A 4 Byte float is archived to a 142 Bytes NSData, a 8 Byte CGPoint uses 260 Bytes when archived to NSData. Keeping the amount of data sent to a minimum is crucial to what I'm doing.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

内心激荡 2024-12-27 13:16:09

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 数据之前添加了一个小标头,它允许我解码数据所属的属性以及其中有多少字节数据部分。这样我就可以将值恢复到远程属性。

Martin Gordon's answer is getting close, but fortunately you don't have to manually parse the objCType string. There's a function that does that: NSGetSizeAndAlignment. From that you get the size/length of the value obtained from getValue:. This question lead me to the solution.

I ended up creating a category for NSData that allows me to create NSData from NSNumber or NSValue objects:

@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

I also add a small header before this NSValue/NSNumber data that allows me to decode which property the data is for and how many bytes are in the data section. With that I can restore the value to the remote property.

御弟哥哥 2024-12-27 13:16:09

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

Since NSValue adopts the NSCoding protocol, you can use the NSCoder subclasses, NSKeyedArchiver and NSKeyedUnarchiver:

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

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

If NSKeyedArchiver and NSKeyedUnarchiver can't be used (for resulting data size issues), then you would have to find the size of the contained value yourself:

- (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

墨洒年华 2024-12-27 13:16:09

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

You can use NSCoder (like NSKeyedArchiver) to archive the data, then send the resulting NSData. On the other side you can unarchive it. There may be some caveats with this (for example http://www.cocoabuilder.com/archive/cocoa/82344-nskeyedarchiver-and-nsvalue.html) esepcially if you are wrapping structs and other non-archiving stuff in NSValue.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文