Question

Today I was reviewing some code and I have been totally fooled. I saw something like this in the code

...
NSNumber *myNumber = [NSNumber numberWithInteger:4];
...
if (myNumber == [NSNumber numberWithInteger:4)
{
...
}
...

When I read that if condition, my first thought was: waw man! how can you do that? You are obviously comparing pointers here, so of course the code in this if will never be executed as you are generating an instance in the right place, so the objects will never be the same!

My (big) surprise was that actually the if was being executed! So I generated some tests to deepen in what what was going on, and I got this:

NSInteger intValue = 5;
NSNumber *anIntNumber = [NSNumber numberWithInteger:intValue];
NSNumber *anotherIntNumber = [NSNumber numberWithInteger:intValue];
NSLog(@"%p vs %p -> %@", anIntNumber, anotherIntNumber, ((anIntNumber == anotherIntNumber) ? @"YES" : @"NO"));

This is the result:

0x7462560 vs 0x7462560 -> YES

So I then remembered that I read somewhere here in stack overflow that constant NSString were stored somewhere in the memory stack when a program begins. So then I run this code to check if the same thing was happening with NSString instances:

NSString *aString = @"FOO";
NSString *anotherString = @"FOO";
NSLog(@"%p vs %p -> %@", aString, anotherString, ((aString == anotherString) ? @"YES" : @"NO"));

This is the result:

0x35cc vs 0x35cc -> YES

So yes, the instance is the same although it was not intentional. What I can imagine with these results is that the system allocates all the NSNumber objects instantiated with constant integers somewhere in the memory stack too.

But my question now is: why do they do that? Isn't the waste of memory more important than the time to generate the instances?

Then I thought: what about the floats? Floating point numbers can not really be compared with the equal operator because of the binary representation and all that stuff. And here is the last test:

float floatValue = 5.41553f;
NSNumber *aFloatNumber = [NSNumber numberWithFloat:floatValue];
NSNumber *anotherFloatNumber = [NSNumber numberWithFloat:floatValue];
NSLog(@"%p vs %p -> %@", aFloatNumber, anotherFloatNumber, ((aFloatNumber == anotherFloatNumber) ? @"YES" : @"NO"));

And here is the only expected result!

0x712f180 vs 0x74299a0 -> NO

What can you say about this? Do you really think this is the best behavior (and the more logic)? Thanks for sharing your knowledge!

Was it helpful?

Solution

The use of the == operator with NSNumber only works in some cases because the NSNumber implementation internally uses a few global instances for the integers 0 through X (12 or 15 or something).

The memory to hold a dozen NSNumber objects in memory is trivial.

Regardless, never use the == operator to check equally. Always use isEqual: (or its variants). Never rely on implementation details or compiler optimizations like this.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top