简体   繁体   中英

NSNumber with if statement in different situations

I know to check whether 2 NSNumbers are the same you need to use ([A isEqualToNumber:B]) instead of (A == B) unlike NSIntegers.

However, I have just realized (A == B) works just fine in simulator and I'd like to know why.

The stranger thing is that on device, (A == B) STILL WORKS as long as the numbers are below 13 and from 13 it will stop working then only ([A isEqualToNumber:B]) works but that is if they're bigger than 12 otherwise (A == B) can still be used.

Why's that??

It's an implementation detail. The == compares the addresses of objects. It just so happens that sometimes different object pointers are assigned to the same actual object when the content is the same and not mutable.

Printing the pointer values is interesting:

NSNumber *n1a = [NSNumber numberWithInt:1];
NSNumber *n1b = [NSNumber numberWithInt:1];
if (n1a == n1b) {
    NSLog(@"Match for 1");
}
NSNumber *n2a = [NSNumber numberWithInt:14];
NSNumber *n2b = [NSNumber numberWithInt:14];
if (n2a == n2b) {
    NSLog(@"Match for 14");
}
NSLog(@"1-%p 2-%p 3-%p 4-%p", n1a, n1b, n2a, n2b);

2016-05-31 11:30:49.118 TestApp[1542:3110206] 1-0x166539d0 2-0x166539d0 3-0x1656ac30 4-0x16587580

On NSNumber there are optimizations in work, because they are used very often.

A. Twintones

The numbers from 0 to 12 are twintones (multitones). 12 is chosen, because hours and month indexes have this range. That means, that they are reused in every creation. In pseudo code (that would not work for many reasons):

id twintones[13];

+ (instancetype)newWithInteger:(int)value
{
  if(value>=0 and value1=12)
  {
    if(!twintones[value])
    {
      twintone[value] = …; // Usual creation
    }
    return twintone[value];
  }
  // Usual creation
}

B. Tagged Pointers

Numbers are sometimes stored as tagged pointers. The basic idea is that a pointer in a 64-bit environment on OS X is always aligned at 16 byte boundaries, making the least 4 bits always 0.

abc…xyz0000

The last bit is used to mark a tagged pointer. This three unused bits left of it can be used to mark a class instead of having a full isa (class) pointer. If you store, ie a 3 in it (binary 011) – or whatever – after a check you can say: "That's an instance of NSNumber storing an integer". The rest of the long word can be used to store the integer value. In such a case the pointer is the object, just encoded tricky. Two equal objects have the same "pointer", but it is no real pointer.

But the most important thing: You should never take any advantage out of this optimizations. They are solely optimizations.

There are great answers here already but note that there are other optimizations at work, for example:

NSNumber *x1 = @24;
NSNumber *x2 = @24;

NSLog(@"test: %@", x1 == x2 ? @"equal" : @"different"); // prints "equal"
NSLog(@"%p", x1);
NSLog(@"%p", x2);

this will work for most numeric values because the literal values can be optimized already during compilation to result in exactly the same instance.

This (documented) optimization is often used to comparison of @YES AND @NO literals. Don't rely on it for numbers though.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM