Добавляет ли NSNumber дополнительные байты к числу, которое он содержит? - PullRequest
0 голосов
/ 19 июля 2009

Я работаю с Objective-C, и мне нужно добавить int из NSArray в NSMutableData (я готовлюсь к отправке данных через соединение). Если я оберну int с помощью NSNumber, а затем добавлю их в NSMutableData, как я узнаю, сколько байтов в NSNumber int? Можно ли будет использовать sizeof (), поскольку, согласно документации Apple, «NSNumber является подклассом NSValue, который предлагает значение в качестве любого скалярного (числового) типа Си».?

Пример:

NSNumber *numero = [[NSNumber alloc] initWithInt:5];

NSMutableData *data = [[NSMutableData alloc] initWithCapacity:0];

[data appendBytes:numero length:sizeof(numero)];

Ответы [ 2 ]

6 голосов
/ 19 июля 2009

цифра не является числовым значением, это указатель на объект, представляющий числовое значение. То, что вы пытаетесь сделать, не сработает, размер всегда будет равен указателю (4 для 32-разрядных платформ и 8 для 64-разрядных), и вы добавите к своим данным некоторое значение указателя мусора, а не число.

Даже если вы попытаетесь разыменовать его, вы не сможете напрямую получить доступ к байтам, поддерживающим номер NSNumber, и ожидать, что он будет работать. То, что происходит, - это внутренняя деталь реализации, которая может варьироваться от выпуска к выпуску или даже между различными конфигурациями одного и того же выпуска (32-битная или 64-битная версия, iPhone против Mac OS X, рука против i386 против PPC). Простая упаковка байтов и отправка их по проводам может привести к тому, что с другой стороны не произойдет десериализация должным образом, даже если вам удалось получить реальные данные.

Вам действительно нужно придумать кодировку целого числа, которое вы можете поместить в свои данные, а затем упаковать и распаковать в них номера NSN. Что-то вроде:

NSNumber *myNumber = ... //(get a value somehow)
int32_t myInteger = [myNumber integerValue]; //Get the integerValue out of the number
int32_t networkInteger = htonl(myInteger); //Convert the integer to network endian
[data appendBytes:&networkInteger sizeof(networkInteger)]; //stuff it into the data

На принимающей стороне вы затем берете целое число и воссоздаете NSNumber с помощью numberWithInteger: после использования ntohl для преобразования его в собственный формат хоста.

Может потребоваться немного больше работы, если вы пытаетесь отправить минимальные представления и т. Д.

Другим вариантом является использование подкласса NSCoder и указание NSNumber кодировать себя с помощью вашего кодера, поскольку это будет нейтральным по отношению к платформе, но это может быть излишним для того, что вы пытаетесь сделать.

2 голосов
/ 19 июля 2009

Во-первых, NSNumber * цифра - это «Указатель на тип NSNumber», а тип NSNumber - это объект Objective-C. В общем, если иное не указано где-то в документации, эмпирическое правило в объектно-ориентированном программировании таково: «Внутренние детали того, как объект выбирает представление своего внутреннего состояния, являются частными для реализации объектов и должны рассматриваться как черные ящик «. Опять же, если в документации не указано, что вы можете поступить иначе, вы не можете предполагать, что NSNumber использует примитивный тип C int для хранения значения int, которое вы ему дали.

Следующее является грубым приближением того, что происходит «за кадром», когда вы appendBytes:numero:

typedef struct {
  Class isa;
  double dbl;
  long long ll;
} NSNumber;

NSNumber *numero = malloc(sizeof(NSNumber));
memset(numero, 0, sizeof(NSNumber));
numero->isa = objc_getClass("NSNumber");

void *bytes = malloc(1024);

memcpy(bytes, numero, sizeof(numero)); // sizeof(numero) == sizeof(void *)

Это делает более ясным, что то, что вы добавляете к объекту NSMutableData data, это первые четыре байта того, на что указывает numero (что для объекта в Obj-C всегда isa, класс объектов). Я подозреваю, что вы «хотели» сделать, это скопировать указатель на экземпляр объекта (значение Numberro), и в этом случае вы должны были использовать &numero. Это проблема, если вы используете GC, поскольку буфер, используемый NSMutableData, не сканируется (т. Е. Система GC больше не будет «видеть» объект и возвращать его, что в значительной степени является гарантией случайного сбоя при немного позже.)

Надеемся, очевидно, что даже если вы поместите указатель на экземплярный объект NSNumber в data, этот указатель имеет значение только в контексте процесса, который его создал. Указатель на этот объект становится еще менее значимым, если вы отправляете этот указатель на другой компьютер - у принимающего компьютера нет (практичного, тривиального) способа чтения памяти, на которую указывает указатель, в отправляющем компьютере.

Поскольку у вас, похоже, возникают проблемы с этой частью процесса, позвольте мне дать рекомендацию, которая сэкономит вам бесчисленные часы отладки некоторых чрезвычайно сложных ошибок реализации, с которыми вы обязательно столкнетесь:

Оставьте всю эту идею, пытаясь отправить необработанные двоичные данные между машинами и просто отправить между ними простую информацию в формате ASCII / UTF-8.

Если вы думаете, что это будет как-то медленно или неэффективно, то позвольте мне порекомендовать вам сначала рассказать об этом, используя упрощенную строковую версию ASCII / UTF-8. Поверьте мне, отладка необработанных двоичных данных не доставляет удовольствия, а способность просто NSLog(@"I got: %@", dataString) на вес золота, когда вы отлаживаете свои неизбежные проблемы. Затем, как только все замерзло, и вы уверены, что вам больше не нужно вносить изменения в то, что вам нужно обменять, «порт» (из-за отсутствия лучшего слова) этой реализации в бинарную версию. если и только если , профилирование с помощью Shark.app идентифицирует его как проблемную область. Для справки, в эти дни я могу scp файл между машинами и насыщать гигабитную ссылку с передачей. scp, вероятно, для сжатия и шифрования данных приходится выполнять примерно в пять тысяч раз больше обработки на байт, чем это простое форматирование со скоростью 80 МБ / с. Тем не менее, на современном оборудовании этого едва хватает, чтобы сдвинуть с места индикатор работы процессора в строке меню.

...