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 netwo
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.
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
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.