【问题标题】:Why NSNumber points to the same address when value are equals?为什么值相等时 NSNumber 指向同一个地址?
【发布时间】:2011-05-15 06:47:05
【问题描述】:

给定以下代码:

int firstInt, secondInt;

firstInt = 5;
secondInt = 5;

NSNumber *firstNumber = [NSNumber numberWithInt:firstInt];
NSNumber *secondNumber = [NSNumber numberWithInt:secondInt];

为什么这两个 NSNumber 实例指向同一个地址?

这让我发疯了!

当然,如果您将 secondInt 更改为“4”,则一切正常。

谢谢, 杰里米

【问题讨论】:

  • 为什么要关心它们是否是同一个对象而不是具有相同值的两个不同对象?
  • 因为即使它们共享相同的值,我也需要将它们作为单独的实例来计算分数。

标签: objective-c pointers ios nsnumber


【解决方案1】:

这可能是编译器优化或实现细节:由于 NSNumber 是不可变的,因此不需要它们是单独的实例。

编辑:可能是考虑它的实现优化。当随后使用相同的整数调用 numberWithInt 时,可能会返回一个单例。

【讨论】:

  • IIRC NSNumber 有一个内部优化,它返回较低整数的单例。实际上,这并不重要,因为重要的是值而不是内存地址。
  • 我的问题是我需要将这些 int 添加到数组中,对它们进行排序(按它们的值)并检索它们的索引(在数组内)。而且由于单例,我得到了错误的索引。
  • 有趣且值得了解,感谢 Abizern 和 Dave。 jchatard:这听起来应该是一个后续问题。
  • @jchatard 快速而肮脏的解决方案是创建一个具有单个 NSInteger 属性的 NSObject 子类。并使用它而不是 NSNumber 将分数添加到数组中。比使用 NSNumber 多一点工作,但它会做你想做的事。更好的解决方案是重新考虑您的设计。
  • Abizern 死了。如果这些数字有某种含义,也许您不应该将它们表示为NSNumbers,而是使用其他对象。
【解决方案2】:

我的设计直觉告诉我,如果分数的标识与其单纯的价值不同,那么你应该对某种分数对象进行排序,而不是简单的 NSNumber。

但是除此之外:在紧要关头,您可以像使用 NSNumber 一样使用普通的 NSValue。取出值需要做更多的工作,但 NSValue 本身没有 NSNumber 对小值所做的实例合并行为。

执行所有三种行为的一些代码:

  // NSValues are always distinct:
  int foo = 5, bar = 5, outfoo, outbar;
  NSValue *one = [NSValue value:&foo withObjCType:@encode(int)];
  NSValue *two = [NSValue value:&bar withObjCType:@encode(int)];

  [one getValue:&outfoo];
  [two getValue:&outbar];
  NSLog(@"one: %@ %x = %d ; two: %@ %x = %d",
        [one class], one, outfoo,
        [two class], two, outbar);

  // by comparison with NSNumber behavior:
  NSNumber *three = [NSNumber numberWithInt:6];
  NSNumber *four = [NSNumber numberWithInt:6];

  NSLog(@"three: %@ %x = %d ; four: %@ %x = %d",
        [three class], three, [three intValue],
        [four class], four, [four intValue]);

  // except when the numbers are big:
  NSNumber *five = [NSNumber numberWithInt:8675309];
  NSNumber *six = [NSNumber numberWithInt:8675309];

  NSLog(@"five: %@ %x = %d ; six: %@ %x = %d",
        [five class], five, [five intValue],
        [six class], six, [six intValue]);

在我的 Mac 上,这会产生如下输出:

one: NSConcreteValue 42a8d0 = 5 ; two: NSConcreteValue 42a920 = 5
three: NSCFNumber 404380 = 6 ; four: NSCFNumber 404380 = 6
five: NSCFNumber 1324d0 = 8675309 ; six: NSCFNumber 106e00 = 8675309

【讨论】:

  • 看起来多年来存储大数字的实现已经得到改进。对于 sn-p NSNumber *five = [NSNumber numberWithInt:8675309]; NSNumber *six = [NSNumber numberWithInt:8675309]; NSLog(@"five: %@ %x = %d ; six: %@ %x = %d", [five class], five, [five intValue], [six class], six, [six intValue]);,我得到具有相同地址的输出:five: __NSCFNumber 845fed27 = 8675309 ; six: __NSCFNumber 845fed27 = 8675309
  • 我认为这是由于 64 位环境附带的“标签指针”:mikeash.com/pyblog/…。 TLDR:任何短于 64 位的 NSNumber 减去标记开销,现在都可以是“单例实例”(实际上只是指针值本身。)[注意你的指针值是奇数,真正的指针不可能是奇数]
【解决方案3】:

因为它们具有相同的hash 值,所以NSNumber 从第一次分配中返回一个缓存指针。

【讨论】:

    猜你喜欢
    • 2019-07-15
    • 2022-11-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-06-28
    • 2014-04-05
    • 2018-11-08
    • 1970-01-01
    相关资源
    最近更新 更多