Как преобразовать NSValue в NSData и обратно?
A имеет число NSValue
(полученное через KVC valueForKey
), которое мне нужно добавить к объекту NSData
, чтобы отправить его по сети с помощью Game Center. Очевидно, мне также понадобится преобразовать NSValue
назад из NSData
для большего количества KVC (setValue:forKey:
).
Я не знаю точного типа каждого NSValue
, поэтому я ограничен использованием интерфейсов, предоставляемых NSValue
и NSData
. Следует отметить, что NSValue
никогда не являются типами указателей.
Я удивлен, что там нет [NSData dataWithValue:value]
и не что-то вроде [value dataWithEncoding:]
или подобного. Но, возможно, я слепой и не вижу очевидного выбора. Я думал об использовании getValue:
для копирования значения в буфер void*
, но как я должен определять длину буфера, кроме как используя objCType
, и сравнивая это со всеми возможными типами?
Как мне это сделать?
Примечание:
NSKeyedArchiver
не может быть и речи, потому что это ужасно неэффективно. Поля 4 байта архивируются до 142 байтов NSData
, а 8 байтов CGPoint
использует 260 байтов при архивировании на NSData
. Сохранение количества данных, присланных до минимума, имеет решающее значение для того, что я делаю.
Ответы
Ответ 1
Ответ Мартина Гордона приближается, но, к счастью, вам не нужно вручную разбирать строку objCType. Там есть функция, которая делает это: NSGetSizeAndAlignment. Из этого вы получите размер/длину значения, полученного от getValue:. Этот вопрос приведет меня к решению.
В итоге я создал категорию для NSData, которая позволяет мне создавать NSData из объектов NSNumber или NSValue:
@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, который позволяет мне декодировать, для какого свойства используются данные, и количества байтов в разделе данных. С этим я могу восстановить значение для свойства remote.
Ответ 2
Так как 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 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 Кодировки типов
Ответ 3
Вы можете использовать NSCoder (например, NSKeyedArchiver) для архивации данных, а затем отправить полученные NSData. С другой стороны, вы можете его разблокировать. Могут быть некоторые предостережения с этим (например http://www.cocoabuilder.com/archive/cocoa/82344-nskeyedarchiver-and-nsvalue.html), если вы обертываете структуры и другие материалы, не связанные с архивированием, в NSValue.