2010-11-24 3 views
7

値が等しい場合、なぜNSNumberが同じアドレスを指しているのですか?

次のコードが与えられます:

int firstInt, secondInt; 

firstInt = 5; 
secondInt = 5; 

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

なぜ地球上の2つのNSNumberインスタンスは同じアドレスを指していますか?

これは私を狂ってしまう!

もちろん、secondIntを「4」と変更すると、すべてが期待通りに機能します。

ありがとう、 Jérémy

+3

なぜあなたは気にしません同じ値ですか? –

+0

同じ値を共有しているにもかかわらず、スコアを計算するために別々のインスタンスにする必要があるためです。 – jchatard

答えて

12

は、これはコンパイラの最適化や実装の詳細のいずれかの可能性があります:のNSNumberは不変であるとして、それらは別々のインスタンスには必要ありません。

EDIT:これについて考えている実装の最適化でしょう。可能性のあるnumberWithIntは、後に同じ整数で呼び出されたときにシングルトンを返します。

+2

IIRC NSNumberには、より小さい整数に対してシングルトンを返す内部最適化があります。実際には、重要な値でありメモリアドレスではないため、重要ではありません。 – Abizern

+7

+1 0-12のNSNumbersはシングルトンです。 –

+0

私の問題は、それらのintを配列に追加し、それらの値でソートし、そのインデックスを配列内で取得する必要があるということです。そして、シングルトンのために、私は間違ったインデックスを取得します。 – jchatard

3

値が同じhashなので、NSNumberは最初の割り当てからキャッシュされたポインタを返します。

+0

ありがとう – jchatard

6

私のデザインの本能は、スコアのアイデンティティが単なる価値とは異なるものとして重要であるならば、プレーンなNSNumberの代わりに何らかの種類のスコアオブジェクトをソートするべきだと言います。

ただし、脇の下:ピンチでは、NSNumberの使用方法と同様にプレーンなNSValueを使用できます。値を取得する作業はもう少しですが、NSValue自体には小さい値に対してはNSNumberのインスタンスの結合動作がありません。

すべての3つの行動を行使するいくつかのコード:

// 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上で、これはのような出力が得られます。彼らは2つの異なるオブジェクト対同じオブジェクトである場合

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

すごい優秀! rgeorgeさん、本当に助けてくれてありがとう。 – jchatard

+0

大きな数字を格納するための実装が長年にわたって改良されているように見えます。スニペットの場合 NSNumber * five = [NSNumber numberWithInt:8675309]; NSNumber * six = [NSNumber numberWithInt:8675309]; NSLog(@ "5:%@%x =%d;%:%@%x =%d"、 [5クラス]、5、[5 intValue]、 [6クラス]、6、[6 intValue ]); ' \t 同じアドレスを出力します:' 5:__NSCFNumber 845fed27 = 8675309; 6:__NSCFNumber 845fed27 = 8675309' –

+0

私はそれが64ビット環境に付属している "タグポインタ"のためだと思う:https://www.mikeash.com/pyblog/friday-qa-2012-07-27-lets -build-tagged-pointers.html。 TLDR:64ビットより短い任意のNSNumberがタギングオーバヘッドを引いたもので、現在は「シングルトンインスタンス」(これは実際にはポインタ値そのものです)になります。[ポインタの値が奇数であることに注意してください。 ] – rgeorge

関連する問題