2009-07-19 2 views
0

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

Пример:

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

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

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

ответ

6

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

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

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

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 кодировать самостоятельно, используя ваш кодер, так как это будет нейтральной платформой, но это может быть излишним для того, что вы пытаетесь сделать.

+0

я все еще получаю ошибку: «предупреждение: проходя аргумент 1 из„appendBytes: длина:“делает указатель из целого числа без броска» –

+0

Это последняя строка должна быть: [appendBytes данные: & networkInteger SizeOf (networkInteger)] ; Обратите внимание на «&». –

+0

Это было ... спасибо. С одной стороны, я просто хотел указать, что в документации Apple говорится, что перед sizeof() будет помещаться «length:». Это часть имени метода. Полный пример в их документации идет примерно так: - (void) appendBytes: (const void *) длина байтов: (NSUInteger) длина Я не хотел ничего говорить, но когда два человека этого не видели, я подумал, что должен упомянуть об этом. –

2

Во-первых, NSNumber * numero - «Указатель на тип 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, класс объектов). Я подозреваю, что вам нужно было скопировать указатель на экземпляр объекта (значение numero), и в этом случае вы должны были использовать &numero. Это проблема, если вы используете GC, поскольку буфер, используемый NSMutableData, не сканируется (то есть система GC больше не «видит» объект и не восстанавливает его, что в значительной степени является гарантией случайного сбоя в некоторых более поздних версиях точка.)

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

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

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

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