Как конвертировать NSValue в NSData и обратно? - PullRequest
11 голосов
/ 09 декабря 2011

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.Сохранение минимального объема отправляемых данных имеет решающее значение для того, что я делаю.

Ответы [ 3 ]

17 голосов
/ 10 декабря 2011

Ответ Мартина Гордона приближается, но, к счастью, вам не нужно анализировать строку 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

Iтакже добавьте небольшой заголовок перед этими данными NSValue / NSNumber, который позволяет мне декодировать, для какого свойства эти данные и сколько байтов в разделе данных.После этого я могу восстановить значение удаленного свойства.

4 голосов
/ 09 декабря 2011

Поскольку 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'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

0 голосов
/ 09 декабря 2011

Вы можете использовать NSCoder (например, NSKeyedArchiver), чтобы заархивировать данные, а затем отправить полученные NSData. С другой стороны вы можете разархивировать его. С этим могут быть некоторые предостережения (например, http://www.cocoabuilder.com/archive/cocoa/82344-nskeyedarchiver-and-nsvalue.html), особенно если вы оборачиваете структуры и другие неархивирующие элементы в NSValue.

...