-1

我正在使用 Objective-C,我需要将 int 从 NSArray 添加到 NSMutableData(我正在准备通过连接发送数据)。如果我用 NSNumber 包装 int,然后将它们添加到 NSMutableData,我将如何找出 NSNumber int 中有多少字节?是否可以使用 sizeof() 因为根据苹果文档,“NSNumber 是 NSValue 的子类,它提供任何 C 标量(数字)类型的值。”?

例子:

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

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

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

2 回答 2

6

numero 不是数值,它是指向表示数值的对象的指针。您尝试执行的操作不起作用,大小将始终等于指针(32 位平台为 4,64 位平台为 8),并且您将在数据中附加一些垃圾指针值而不是数字。

即使您尝试取消引用它,您也不能直接访问支持 NSNumber 的字节并期望它工作。正在发生的是内部实现细节,可能会因版本而异,甚至在同一版本的不同配置之间(32 位与 64 位,iPhone 与 Mac OS X,arm 与 i386 与 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

然后在接收端,您取出整数并使用 numberWithInteger: 重新创建一个 NSNumber:在使用 ntohl 将其转换为本机主机格式之后。

如果您尝试发送最少的表示等,可能需要更多的工作。

另一种选择是使用 NSCoder 子类并告诉 NSNumber 使用您的编码器对其自身进行编码,因为这将是平台中立的,但对于您正在尝试做的事情来说可能是矫枉过正。

于 2009-07-19T02:02:01.997 回答
2

首先,NSNumber *numero 是“指向 NSNumber 类型的指针”,而 NSNumber 类型是一个 Objective-C 对象。一般来说,除非在文档中特别说明,否则面向对象编程的经验法则是“对象如何选择表示其内部状态的内部细节对于对象实现来说是私有的,应该被视为黑色盒子。” 同样,除非文档说明您可以这样做,否则您不能假设 NSNumber 正在使用 C 原始类型 ofint来存储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对象的是 ever指向data的内容的前四个字节(对于 Obj-C 中的对象来说,它始终是 objects 类)。我怀疑您“想要”做的是将指针复制到实例化对象(numero 的值),在这种情况下您应该使用. 如果您使用 GC 时使用的缓冲区未被扫描,这将是一个问题(即,GC 系统将不再“看到”对象并回收它,这几乎可以保证以后随机崩溃.)numeroisa&numeroNSMutableData

希望很明显,即使您将指向实例化NSNumber对象的指针放入 to data,该指针也仅在创建它的进程的上下文中有意义。如果您将该指针发送到另一台计算机,则指向该对象的指针甚至更没有意义 - 接收计算机没有(实际的,琐碎的)方法来读取指针在发送计算机中指向的内存。

由于您似乎在这部分过程中遇到问题,让我提出一个建议,它将为您节省无数小时调试一些您一定会遇到的极其困难的实现错误:

放弃尝试在机器之间发送原始二进制数据并在它们之间发送简单的 ASCII/UTF-8 格式信息的整个想法。

如果您认为这会很慢或效率低下,那么我建议您首先使用简化的 ASCII/UTF-8 字符串化版本来启动所有内容。相信我,调试原始二进制数据并不好玩,NSLog(@"I got: %@", dataString)当你调试不可避免的问题时,这种能力是物超所值的。然后,一旦一切都已经凝固,并且您确信您不需要对需要交换的内容进行任何更改,“移植”(因为没有更好的词)该实现到仅二进制版本当且仅当使用 Shark.app 进行分析将其识别为问题区域。作为参考,这些天我可以scp在机器之间的文件和饱和传输的千兆链接。 scp在传输 80MB/秒时,每个字节压缩和加密数据的处理量可能比这种简单的字符串化要多 5000 倍。然而,在现代硬件上,这几乎不足以让我的菜单栏中运行的 CPU 计量器发生变化。

于 2009-07-19T03:57:18.567 回答