[英]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:
). 显然,我还需要将
NSValue
从NSData
转换回更多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
的确切类型,所以我仅限于使用NSValue
和NSData
提供的接口。 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. 注意:
NSKeyedArchiver
是NSKeyedArchiver
的,因为它效率极低。 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
子类NSKeyedArchiver
和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: 如果不能使用
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 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.