【问题标题】:Does NSNumber add any extra bytes to the number it holds?NSNumber 是否会在它持有的数字上添加任何额外的字节?
【发布时间】:2009-07-19 01:39:46
【问题描述】:

我正在使用 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)];

【问题讨论】:

    标签: objective-c integer byte nsdata nsnumber


    【解决方案1】:

    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 使用您的编码器对其自身进行编码,因为这将是平台中立的,但对于您正在尝试做的事情来说可能有点矫枉过正。

    【讨论】:

    • 我仍然收到错误消息:“警告:传递 'appendBytes:length:' 的参数 1 使指针从整数而不进行强制转换”
    • 最后一行应该是:[data appendBytes:&networkInteger sizeof(networkInteger)];注意“&”。
    • 做到了...谢谢。在旁注中,我只是想指出,在苹果的文档中,它说在 sizeof() 之前放置“长度:”。它是方法名称的一部分。他们文档中的完整示例是这样的; - (void)appendBytes:(const void *)bytes length:(NSUInteger)length 我本来不想说什么,但是当两个人没有看到这个的时候,我想我应该提一下。
    • 感谢 Jens,在浏览器文本字段中编写代码很容易出错 ;-)
    【解决方案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,该指针也仅在创建它的进程的上下文中才有意义。如果您将该指针发送到另一台计算机,那么指向该对象的指针就更没有意义了——接收计算机没有(实际的、微不足道的)方法来读取指针指向的发送计算机中的内存。

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

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

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-04-04
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多