NSNumber 是否会向它所保存的数字添加任何额外的字节?

发布于 2024-07-27 04:33:12 字数 434 浏览 10 评论 0原文

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

I'm working with Objective-C and I need to add int's from a NSArray to a NSMutableData (I'm preparing a to send the data over a connection). If I wrap the int's with NSNumber and then add them to NSMutableData, how would I find out how many bytes are in the NSNumber int? Would it be possible to use sizeof() since according to the apple documentation, "NSNumber is a subclass of NSValue that offers a value as any C scalar (numeric) type."?

Example:

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

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

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

笛声青案梦长安 2024-08-03 04:33:12

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 使用您的编码器对其自身进行编码,因为这将是平台中立的,但可能有点过头了为了你想做的事。

numero is not a numeric value, it is a pointer to a an object represting a numeric value. What you are trying to do won't work, the size will always be equal to a pointer (4 for 32 bit platforms and 8 for 64 bit), and you will append some garbage pointer value to your data as opposed to the number.

Even if you were to try to dereference it, you cannot directly access the bytes backing an NSNumber and expect it to work. What is going on is an internal implementation detail, and may vary from release to release, or even between different configurations of the same release (32 bit vs 64 bit, iPhone vs Mac OS X, arm vs i386 vs PPC). Just packing up the bytes and sending them over the wire may result in something that does not deserialize properly on the other side, even if you managed to get to the actual data.

You really need to come up with an encoding of an integer you can put into your data and then pack and unpack the NSNumbers into that. Something like:

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

On the receiving side you then grab out the integer and recreate an NSNumber with numberWithInteger: after using ntohl to convert it to native host format.

It may require a bit more work if you are trying to send minimal representations, etc.

The other option is to use an NSCoder subclass and tell the NSNumber to encode itself using your coder, since that will be platform neutral, but it may be overkill for what you are trying to do.

原来分手还会想你 2024-08-03 04:33:12

首先,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 的内容更加清晰> object datanumero 所指向的前四个字节(对于 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 仪表。

First, NSNumber *numero is "A pointer to a NSNumber type", and the NSNumber type is an Objective-C object. In general, unless specifically stated somewhere in the documentation, the rule of thumb in object-oriented programming is that "The internal details of how an object chooses to represent its internal state is private to the objects implementation, and should be treated as a black box." Again, unless the documentation says you can do otherwise, you can't assume that NSNumber is using a C primitive type of int to store the int value you gave it.

The following is a rough approximation of what's going on 'behind the scenes' when you 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 *)

This makes it a bit more clear that what you're appending to the NSMutableData object data is the first four bytes of what ever numero is pointing to (which, for an object in Obj-C is always isa, the objects class). I suspect what you "wanted" to do was copy the pointer to the instantiated object (the value of numero), in which case you should have used &numero. This is a problem if you're using GC as the buffer used by NSMutableData is not scanned (ie, the GC system will no longer "see" the object and reclaim it, which is pretty much a guarantee for a random crash at some later point.)

It's hopefully obvious that even if you put the pointer to the instantiated NSNumber object in to data, that pointer only has meaning in the context of the process that created it. A pointer to that object is even less meaningful if you send that pointer to another computer- the receiving computer has no (practical, trivial) way to read the memory that the pointer points to in the sending computer.

Since you seem to be having problems with this part of the process, let me make a recommendation that will save you countless hours of debugging some extremely difficult implementation bugs you're bound to run in to:

Abandon this entire idea of trying to send raw binary data between machines and just send simple ASCII/UTF-8 formatted information between them.

If you think that this is some how going to be slow, or inefficient, then let me recommend that you bring every thing up using a simplified ASCII/UTF-8 stringified version first. Trust me, debugging raw binary data is no fun, and the ability to just NSLog(@"I got: %@", dataString) is worth its weight in gold when you're debugging your inevitable problems. Then, once everything has gelled, and you're confident that you don't need to make any more changes to what it is you need to exchange, "port" (for lack of a better word) that implementation to a binary only version if, and only if, profiling with Shark.app identifies it as a problem area. As a point of reference, these days I can scp a file between machines and saturate a gigabit link with the transfer. scp probably has to do about five thousand times as much processing per byte to compress and encrypt the data than this simple stringification all while transferring 80MB/sec. Yet on modern hardware this is barely enough to budge the CPU meter running in my menu bar.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文