Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

iOS - Why Does It Work When I Compare Two NSNumbers With "=="?

In my app, I accidentally used "==" when comparing two NSNumber objects like so:

NSNumber *number1;
NSNumber *number2;

Later on, after these objects' int values were set, I accidentally did this:

if (number1 == number2) {
    NSLog(@"THEY'RE EQUAL");
}

And, confusingly, it worked! I could have sworn I was taught to do it this way:

if (number1.intValue == number2.intValue) {
    NSLog(@"THEY'RE EQUAL");
}

How did using "==" between the two NSNumber objects work, and why? Does that mean it's okay to compare them that way, or was it just a fluke and this is generally not guaranteed to work every time? It really confused me :(

like image 414
Rafi Avatar asked Dec 05 '15 11:12

Rafi


3 Answers

It's not a fluke. It's due to the tagged pointers feature of the Objective-C runtime while using an ARM64 CPU.

In Mac OS X 10.7, Apple introduced tagged pointers. Tagged pointers allow certain classes with small amounts of per-instance data to be stored entirely within the pointer. This can eliminate the need for memory allocations for many uses of classes like NSNumber, and can make for a good performance boost.[…] on ARM64, the Objective-C runtime includes tagged pointers, with all of the same benefits they've brought to the Mac

Source

like image 104
Marcio Avatar answered Nov 14 '22 21:11

Marcio


That is possibly a fluke.

From NSHipster :

Two objects may be equal or equivalent to one another, if they share a common set of observable properties. Yet, those two objects may still be thought to be distinct, each with their own identity. In programming, an object’s identity is tied to its memory address.

Its possible that your statement evaluated to YES because number1 and number2 were pointing to the same object. This would not work if they had the same value but were two different objects.

The obvious reason NSNumber variables would point to the same would be that you explicitly assigned one to the other, like so:

number1 = number2;

But there's one other thing. From this answer :

This is likely either a compiler optimisation or an implementation detail: as NSNumber is immutable there's no need for them be separate instances. probably an implementation optimisation thinking about it. Likely numberWithInt returns a singleton when called subsequently with the same integer.

But anyways, its safest to use isEqualToNumber:, as there is no telling what other "things" are lurking in the depths of code that may or may not cause it to evaluate YES

From RyPress :

While it’s possible to directly compare NSNumber pointers, the isEqualToNumber: method is a much more robust way to check for equality. It guarantees that two values will compare equal, even if they are stored in different objects.

like image 24
ShahiM Avatar answered Nov 14 '22 21:11

ShahiM


There two concepts of equality at work here:

  • Object identity: Comparing that two pointers point to the same objects
  • Value equality: That the contents of two objects are equal.

In this case, you want value equality. In your code you declare two pointers to NSNumber objects:

NSNumber *number1;
NSNumber *number2;

But at no point show assignment of a value to them. This means the contents of the pointers can be anything, and quite by chance you have two pointers pointing to the memory locations (not necessarily the same ones) where (number1.intValue == number2.intValue) happens to be true.

You can expect the behaviour to change in unstable ways - for instance as soon as you add any more code.

like image 2
marko Avatar answered Nov 14 '22 21:11

marko