代码之家  ›  专栏  ›  技术社区  ›  Josh Bradley

NSNumber是否在其持有的数字上添加任何额外字节?

  •  -1
  • Josh Bradley  · 技术社区  · 16 年前

    我正在使用Objective-C,我需要将NSArray中的int添加到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)];
    
    2 回复  |  直到 16 年前
        1
  •  6
  •   Louis Gerbarg    16 年前

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

    即使尝试取消引用它,也无法直接访问支持NSNumber的字节并期望它工作。这是一个内部实现细节,可能会因版本而异,甚至在同一版本的不同配置(32位对64位,iPhone对Mac OS X,arm对i386对PPC)之间也会有所不同。仅仅打包字节并通过线路发送它们可能会导致另一端无法正确反序列化某些内容,即使您设法获得了实际数据。

    你真的需要想出一个整数的编码,你可以把它放进你的数据中,然后把这些数字打包和解包到里面。比如:

    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
    

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

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

        2
  •  2
  •   johne    16 年前

    首先,NSNumber*NUMEO是“指向NSNumber类型的指针”,NSNumber类型是Objective-C对象。一般来说,除非文档中有明确说明,否则面向对象编程的经验法则是“对象选择如何表示其内部状态的内部细节对于对象实现来说是私有的,应该被视为黑盒。”同样,除非文档中说您可以做其他事情,您不能假设NSNumber使用的是C基元类型 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用作所使用的缓冲区,则这是一个问题 不会被扫描(也就是说,GC系统将不再“看到”对象并回收它,这在很大程度上保证了以后会发生随机崩溃。)

    NSNumber 反对 数据 ,该指针仅在创建它的进程的上下文中有意义。如果您将指向该对象的指针发送到另一台计算机,则该对象的指针的意义就更小了-接收计算机没有(实用的、琐碎的)方法读取指针指向发送计算机的内存。

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

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

    如果您认为这将是一个缓慢或低效的过程,那么我建议您首先使用简化的ASCII/UTF-8字符串化版本来解决所有问题。相信我,调试原始二进制数据毫无乐趣,而且能够 NSLog(@"I got: %@", dataString) 当你调试不可避免的问题时,它是值得的。然后,一旦一切都凝固了,并且您确信不需要对它进行任何更改,您需要将“端口”(因为没有更好的词)实现转换为仅二进制版本,如果, 除非 ,使用Shark.app进行评测,将其识别为问题区域。作为参考,这些天我可以 scp 机器之间的一个文件,通过传输使千兆链路饱和。 scp 与此简单的字符串化相比,在传输80MB/秒的数据时,每个字节可能要做大约5000倍的处理来压缩和加密数据。然而,在现代硬件上,这几乎不足以让我的菜单栏中运行的CPU表动起来。