简体   繁体   English

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

[英]How to convert NSValue to NSData and back?

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. 我需要附加多个NSValue (通过KVC valueForKey获得),以便使用Game Center通过网络将其发送给NSData对象。 Obviously I will also need to convert the NSValue back from NSData for more KVC ( setValue:forKey: ). 显然,我还需要将NSValueNSData转换回更多KVC( setValue:forKey: NSValue

I don't know the exact type of each NSValue , so I'm limited to using the interfaces provided by NSValue and NSData . 我不知道每个NSValue的确切类型,所以我仅限于使用NSValueNSData提供的接口。 I should mention that the NSValue are never pointer types. 我应该提到NSValue绝不是指针类型。

I'm surprised that there's no [NSData dataWithValue:value] and neither something like [value dataWithEncoding:] or similar. 我很惊讶,没有[NSData dataWithValue:value] ,也没有类似[value dataWithEncoding:]类的东西。 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? 我考虑过使用getValue:将值复制到void*缓冲区中,但是除了使用objCType并将其与所有可能的类型进行比较之外,我应该如何确定缓冲区的长度?

How should I go about this? 我应该怎么做?

NOTE: NSKeyedArchiver is out of the question because it is terribly inefficient. 注意: NSKeyedArchiverNSKeyedArchiver的,因为它效率极低。 A 4 Byte float is archived to a 142 Bytes NSData , a 8 Byte CGPoint uses 260 Bytes when archived to NSData . 将4字节的浮点数归档到142字节的NSData ,将8字节的CGPoint归档到NSData时使用260字节。 Keeping the amount of data sent to a minimum is crucial to what I'm doing. 保持发送的数据量最少对我正在做的事情至关重要。

Martin Gordon's answer is getting close, but fortunately you don't have to manually parse the objCType string. Martin Gordon的答案越来越近了,但是幸运的是,您不必手动解析objCType字符串。 There's a function that does that: NSGetSizeAndAlignment . 有一个函数可以做到这一点: NSGetSizeAndAlignment From that you get the size/length of the value obtained from getValue:. 由此,您可以获得从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: 我最终为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

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. 我还在此NSValue / NSNumber数据之前添加了一个小标头,该标头使我可以解码数据所针对的属性以及数据部分中的字节数。 With that I can restore the value to the remote property. 这样,我可以将值恢复到remote属性。

Since NSValue adopts the NSCoding protocol, you can use the NSCoder subclasses, NSKeyedArchiver and NSKeyedUnarchiver : 由于NSValue采用NSCoding协议,因此可以使用NSCoder子类NSKeyedArchiverNSKeyedUnarchiver

- (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: 如果不能使用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 [1]: Objective-C类型编码

You can use NSCoder (like NSKeyedArchiver) to archive the data, then send the resulting NSData. 您可以使用NSCoder(如NSKeyedArchiver)来归档数据,然后发送结果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. 对此可能会有一些警告(例如, http://www.cocoabuilder.com/archive/cocoa/82344-nskeyedarchiver-and-nsvalue.html ),尤其是在NSValue中包装结构和其他非归档内容时。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM