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

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 :(

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

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.

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.

Of course you can compare two NSNumber* with ==. This will tell you whether the pointers are equal. Of course if the pointers are equal then the values must be the same. The values can be the same without the pointers being equal.
Now you need to be aware that MaxOS X and iOS do some significant optimisations to save storage, especially in 64 bit code. Many NSNumbers representing the same integer value will actually be the same pointer.
NSNumber* value1 = [[NSNumber alloc] initWithInteger:1];
NSNumber* value2 = [[NSNumber alloc] initWithInteger:1];
These will be the same pointers. In 64 bit, many others will be the same pointers. There are only ever two NSNumber objects with boolean values. There is only ever one empty NSArray object, and only one [NSNull null] object.
Don't let that lull you into any wrong assumptions. If you want to see if two NSNumbers have the same value, use isEqualToNumber: You may say "if (number1 == number2 || [number1 isEqualToNumber:number2])"; that's fine (didn't check if I got the names right).

Related

Comparing NSNumber instances with isEqualToNumber

Yes, I've read the other posts on stackoverflow about comparing NSNumber and none of them seem to quite address this particular situation.
This solution was particularly bad ... NSNumber compare: returning different results
Because the suggested solution doesn't work at all.
Using abs(value1 - value2) < tolerance is flawed from the start because fractional values are stripped off, making the tolerance irrelevant.
And from Apple documentation... NSNumber explicitly doesn't guarantee that the returned type will match the method used to create it. In other words, if you're given an NSNumber, you have no way of determining whether it contains a float, double, int, bool, or whatever.
Also, as best I can tell, NSNumber isEqualToNumber is an untrustworthy method to compare two NSNumbers.
So given these definitions...
NSNumber *float1 = [NSNumber numberWithFloat:1.00001];
NSNumber *double1 = [NSNumber numberWithDouble:1.00001];
If you run the debugger and then do 2 comparisons of these identical numbers using ==, one fails, and the other does not.
p [double1 floatValue] == [float1 floatValue] **// returns true**
p [double1 doubleValue] == [float1 doubleValue] **// returns false**
If you compare them using isEqualToNumber
p [float1 isEqualToNumber:double1] **// returns false**
So if isEqualToNumber is going to return false, given that the creation of an NSNumber is a black box that may give you some other type on the way out, I'm not sure what good that method is.
So if you're going to make a test for dirty, because an existing value has been changed to a new value... what's the simplest way to do that that will handle all NSNumber comparisons.. not just float and double, but all NSNumbers?
It seems that converting to a string value, then compariing would be most useful, or perhaps a whole lot of extra code using NSNumberFormatter.
What are your thoughts?
It is not possible to reliably compare two IEEE floats or doubles. This has nothing to do with NSNumber. This is the nature of floating point. This is discussed in the context of simple C types at Strange problem comparing floats in objective-C. The only correct way to compare floating point numbers is by testing against a tolerance. I don't know what you mean by "fractional values are stripped off." Some digits are always lost in a floating point representation.
The particular test value you've provided demonstrates the problems quite nicely. 1.00001 cannot be expressed precisely in a finite number of binary digits. Wolfram Alpha is a nice way to explore this, but as a double, 1.00001 rounds to 1.0000100000000001. As a float, it rounds to 1.00001001. These numbers, obviously, are not equal. If you roundtrip them in different ways, it should not surprise you that isEqualToNumber: fails. This should make clear why your two floatValue calls do turn out to be equal. Rounded to the precision of float, they're "close enough."
If you want to compare floating point numbers, you must compare against an epsilon. Given recent advances in compiler optimization, even two identical pieces of floating point C code can generate slightly different values in their least-significant digits if you use -Ofast (we get big performance benefits by allowing that).
If you need some specific number of significant fractional digits, then it is usually better to work in fixed point notation. Just scale everything by the number of digits you need and work in integers. If you need floating point, but just want base-10 to work well (rather than base-2), then use NSDecimalNumber or NSDecimal. That will move your problems to things that round badly in base-10. But if you're working in floating point, you must deal with rounding errors.
For a much more extensive discussion, see "What Every Programmer Should Know About Floating-Point Arithmetic."

When would you use NSNumber literal to create encapsulated character values?

I'm just going through Apple's iOS development tutorial at the moment and reading the chapter on the Foundation framework and value objects.
Just on the NSNumber class, it says:
You can even use NSNumber literals to create encapsulated Boolean and
character values.
NSNumber *myBoolValue = #YES; NSNumber *myCharValue = #'V';
I'm just wondering, when, or why, or in what scenario, might you want to use NSNumber for a character value rather than using NSString, say?
An NSNumber is useful for encapsulating primitive values to be inserted into Objective-C collection classes such as NSArray, NSSet, NSDictionary, etc.
Image a scenario where you would want to iterate over each character in an ASCII string and extract a unique set of vowels used. You can evaluate each character and add it to an NSMutableSet. To do so, you would need to encapsulate each character in an NSNumber as NSMutableSet expects an Objective-C object. This is just one example, but the concept applies to many situations where primitives need to be added into a collection.
Well, one case is where you're using KVC to set a value for a key, and the property type is char:
[object setValue:#'a' forKey:someCharPropertyName];
You can use NSNumber with characters to return its ASCII Code, so V would return 86.
I don't think many people use it that much, but you could probably use it for character validation. I think it just one of those things where Apple went, yeah, lets put that in for the heck of it.
It's really not used for much else. The #YES and #NO is the same as YES and NO, so its kinda inelegant in some places.

How are identical NSStrings determined in Objective-C?

Consider the following code:
+ (NSString *)helloString
{
return #"hello";
}
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
NSString *a = [AppDelegate helloString];
NSString *b = [AppDelegate helloString];
NSLog(#"%#", a == b ? #"yes" : #"no");
abort();
}
On my machine the result is always "yes". Does it mean that the NSString literal #"hello" is always the same "object" in Objective-C runtime?
Actually my original purpose is to use an uniquely identifiable object to bind in NSNotification's postNotificationName:object: method. I plan to use a NSString literal to act as the object. Is it safe/recommended to do so?
Historically, NSString literals were guaranteed to be unique within a translation unit, and were often unique even between translation units in practice. The current documentation no longer makes this claim as far as I know, and the Clang docs recommend against relying on it.
If you want a string that's guaranteed to always be the same object, you can simply assign a string to a global constant. All references to that constant will definitely yield the same object.
With regard to NSNotification, though, I wouldn't use such a string as the object. The semantics of NSNotification say the the object argument should be the object that triggered the notification — where it conceptually "comes from." Other information associated with the notification would make more sense in the user info dictionary.
[a isEqualToString: b] compares two strings a and b, and returns YES if the contents is the same. This works if they are the same object, or different objects, or one is an NSString and one is an NSMutableString, or one is one of the many classes that behave like strings. If a is nil the result is NO, if a is not nil but b is nil you get a crash.
Comparing strings with a == b is interesting: If a and b are both nil the result is YES, one nil but not the other returns NO. If a and b are the same string because you have the same string literal or the same NSString object assigned to a and b, the result is YES. In your example, the "helloString" method always returns the same literal. Not just a literal with the same characters, but the same literal.
If you use literals with the same characters, they may or may not be the same. No guarantees. If you use the copy method, the result may be the same as the original or not. No guarantees. All in all, == or != for NSString is not very useful. It's only useful to compare nil vs. not nil, or if you know exactly which string was assigned.
BTW >=, >, <=, < give undefined behaviour if the strings are not the same, so they are completely useless.
That depends on the compiler implementation, which in theory could change from time to time, so it is not reliable. If you need to make sure that the NSString pointer is always the same, you should use a constant.
const NSString* kSomeConstantName = #"ConstantValue";
Just use the following Code:
[a isEqualToString:b]
Actually in your case #"hello" is always returned same pointer as mostly all language including objective c use string interning for string literals which means for same string literal it reruns single object.
But this is not the case which is always true means two strings with same characters may have different references if you allocate them differently by alloc keyword.
So always use isEqualToString: if you need to check string equality for its contents.== is used for reference equality(locations in memory) which may or may not different for same contents strings.
Does it mean that the NSString literal #"hello" is always the same "object" in Objective-C runtime?
I wouldn't count on it being true in all cases. Strings should be compared with -isEqualToString:.
Actually my original purpose is to use an uniquely identifiable object to bind in NSNotification's postNotificationName:object: method.
That seems like a misuse of the API. In most cases, you should just pass the object that's posting the notification.

need of pointer objects in objective c

A very basic question .. but really very important to understand the concepts..
in c++ or c languages, we usually don't use pointer variables to store values.. i.e. values are stored simply as is in:
int a=10;
but here in ios sdk, in objective c, most of the objects which we use are initialized by denoting a pointer with them as in:
NSArray *myArray=[NSArray array];
So,the question arises in my mind ,that, what are the benefit and need of using pointer-objects (thats what we call them here, if it is not correct, please, do tell)..
Also I just get confused sometimes with memory allocation fundamentals when using a pointer objects for allocation. Can I look for good explanations anywhere?
in c++ or c languages, we usually don't use pointer variables to store values
I would take that "or C" part out. C++ programmers do frown upon the use of raw pointers, but C programmers don't. C programmers love pointers and regard them as an inevitable silver bullet solution to all problems. (No, not really, but pointers are still very frequently used in C.)
but here in ios sdk, in objective c, most of the objects which we use are initialized by denoting a pointer with them
Oh, look closer:
most of the objects
Even closer:
objects
So you are talking about Objective-C objects, amirite? (Disregard the subtlety that the C standard essentially describes all values and variables as an "object".)
It's really just Objective-C objects that are always pointers in Objective-C. Since Objective-C is a strict superset of C, all of the C idioms and programming techniques still apply when writing iOS apps (or OS X apps, or any other Objective-C based program for that matter). It's pointless, superfluous, wasteful, and as such, it is even considered an error to write something like
int *i = malloc(sizeof(int));
for (*i = 0; *i < 10; ++*i)
just because we are in Objective-C land. Primitives (or more correctly "plain old datatypes" with C++ terminology) still follow the "don't use a pointer if not needed" rule.
what are the benefit and need of using pointer-objects
So, why they are necessary:
Objective-C is an object-oriented and dynamic language. These two, strongly related properties of the language make it possible for programmers to take advantage of technologies such as polymorphism, duck-typing and dynamic binding (yes, these are hyperlinks, click them).
The way these features are implemented make it necessary that all objects be represented by a pointer to them. Let's see an example.
A common task when writing a mobile application is retrieving some data from a server. Modern web-based APIs use the JSON data exchange format for serializing data. This is a simple textual format which can be parsed (for example, using the NSJSONSerialization class) into various types of data structures and their corresponding collection classes, such as an NSArray or an NSDictionary. This means that the JSON parser class/method/function has to return something generic, something that can represent both an array and a dictionary.
So now what? We can't return a non-pointer NSArray or NSDictionary struct (Objective-C objects are really just plain old C structs under the hoods on all platforms I know Objective-C works on), because they are of different size, they have different memory layouts, etc. The compiler couldn't make sense of the code. That's why we return a pointer to a generic Objective-C object, of type id.
The C standard mandates that pointers to structs (and as such, to objects) have the same representation and alignment requirements (C99 6.2.5.27), i. e. that a pointer to any struct can be cast to a pointer to any other struct safely. Thus, this approach is correct, and we can now return any object. Using runtime introspection, it is also possible to determine the exact type (class) of the object dynamically and then use it accordingly.
And why they are convenient or better (in some aspects) than non-pointers:
Using pointers, there is no need to pass around multiple copies of the same object. Creating a lot of copies (for example, each time an object is assigned to or passed to a function) can be slow and lead to performance problems - a moderately complex object, for example, a view or a view controller, can have dozens of instance variables, thus a single instance may measure literally hundreds of bytes. If a function call that takes an object type is called thousands or millions of times in a tight loop, then re-assigning and copying it is quite painful (for the CPU anyway), and it's much easier and more straightforward to just pass in a pointer to the object (which is smaller in size and hence faster to copy over). Furthermore, Objective-C, being a reference counted language, even kind of "discourages" excessive copying anyway. Retaining and releasing is preferred over explicit copying and deallocation.
Also I just get confused sometimes with memory allocation fundamentals when using a pointer objects for allocation
Then you are most probably confused enough even without pointers. Don't blame it on the pointers, it's rather a programmer error ;-)
So here's...
...the official documentation and memory management guide by Apple;
...the earliest related Stack Overflow question I could find;
...something you should read before trying to continue Objective-C programming #1; (i. e. learn C first)
...something you should read before trying to continue Objective-C programming #2;
...something you should read before trying to continue Objective-C programming #3;
...and an old Stack Overflow question regarding C memory management rules, techniques and idioms;
Have fun! :-)
Anything more complex than an int or a char or similar is usually passed as
pointers even in C. In C you could of course pass around a struct of data
from function to function but this is rarely seen.
Consider the following code:
struct some_struct {
int an_int;
char a_char[1234];
};
void function1(void)
{
struct some_struct s;
function2(s);
}
void function2(struct some_struct s)
{
//do something with some_struct s
}
The some_struct data s will be put on the stack for function1. When function2
is called the data will be copied and put on the stack for use in function2.
It requires the data to be on the stack twice as well as the data to be
copied. This is not very efficient. Also, note that changing the values
of the struct in function2 will not affect the struct in function1, they
are different data in memory.
Instead consider the following code:
struct some_struct {
int an_int;
char a_char[1234];
};
void function1(void)
{
struct some_struct *s = malloc(sizeof(struct some_struct));
function2(s);
free(s);
}
void function2(struct some_struct *s)
{
//do something with some_struct s
}
The some_struct data will be put on the heap instead of the stack. Only
a pointer to this data will be put on the stack for function1, copied in the
call to function2 another pointer put on the stack for function2. This is a
lot more efficient than the previous example. Also, note that any changes of
the data in the struct made by function2 will now affect the struct in
function1, they are the same data in memory.
This is basically the fundamentals on which higher level programming languages
such as Objective-C is built and the benefits from building these languages
like this.
The benefit and need of pointer is that it behaves like a mirror. It reflects what it points to. One main place where points could be very useful is to share data between functions or methods. The local variables are not guaranteed to keep their value each time a function returns, and that they’re visible only inside their own function. But you still may want to share data between functions or methods. You can use return, but that works only for a single value. You can also use global variables, but not to store your complete data, you soon have a mess. So we need some variable that can share data between functions or methods. There comes pointers to our remedy. You can just create the data and just pass around the memory address (the unique ID) pointing to that data. Using this pointer the data could be accessed and altered in any function or method. In terms of writing modular code, that’s the most important purpose of a pointer— to share data in many different places in a program.
The main difference between C and Objective-C in this regard is that arrays in Objective-C are commonly implemented as objects. (Arrays are always implemented as objects in Java, BTW, and C++ has several common classes resembling NSArray.)
Anyone who has considered the issue carefully understands that "bare" C-like arrays are problematic -- awkward to deal with and very frequently the source of errors and confusion. ("An array 'decays' to a pointer" -- what is that supposed to mean, anyway, other than to admit in a backhanded way "Yes, it's confusing"??)
Allocation in Objective-C is a bit confusing in large part because it's in transition. The old manual reference count scheme could be easily understood (if not so easily dealt with in implementations), but ARC, while simpler to deal with, is far harder to truly understand, and understanding both simultaneously is even harder. But both are easier to deal with than C, where "zombie pointers" are almost a given, due to the lack of reference counting. (C may seem simpler, but only because you don't do things as complex as those you'd do with Objective-C, due to the difficulty controlling it all.)
You use a pointer always when referring to something on the heap and sometimes, but usually not when referring to something on the stack.
Since Objective-C objects are always allocated on the heap (with the exception of Blocks, but that is orthogonal to this discussion), you always use pointers to Objective-C objects. Both the id and Class types are really pointers.
Where you don't use pointers are for certain primitive types and simple structures. NSPoint, NSRange, int, NSUInteger, etc... are all typically accessed via the stack and typically you do not use pointers.

NSNumber primitive value equality vs isEqualToNumber with Obj-C Literals

Now that we have NSNumber literals with compiler support in Objective-C, is there a preferred way to compare an NSNumber to a known integer value?
The old way is
[myNumber integerValue] == 5
Now we can do [myNumber isEqualToNumber:#5] or even [myNumber isEqualToNumber:#(someVariable)].
Is there an advantage to the isEqualToNumber: method, or should we stick with integerValue unless the value to compare to is already an NSNumber?
One advantage I can see is if someVariable changes from an NSInteger to a CGFloat, no code changes will be needed for the new way.
The new way is really a new syntax around the old
[myNumber isEqualToNumber:[NSNumber numberWithInt:5]]
which requires an extra call of numberWithInt:; essentially, we're comparing a solution with a single dispatch and zero allocations to a solution with two dispatches, and possibly an allocation/deallocation.
If you do this comparison outside a tight loop, it wouldn't matter. But if you do it in a really tight loop, perhaps while drawing something, you may see a slowndown. That's why I'd stay with the old method of
[myNumber integerValue] == 5
The "old way" is one method call plus the comparison operator against two basic types.
The "new way" is one method call plus an extra object creation.
So the old way is more efficient. But unless this is done in a high performance loop (or something similar), the difference is negligible.
As you stated, the new way may be more flexible with regard to the specific type of number.
Personally, I'd choose the form that you find more readable and maintainable unless you have a clear and specific performance issue to deal with.
Though you may have a specific reason to compare a float value or an integer value regardless the original value. In this case, the old way is better because the type of comparison is clear.
Short answer: [myNumber integerValue] == 5 is still best.
Long (but you probably shouldn't care) answer: Starting iOS 5, "some" NSNumbers are implemented using tagged pointers (quick google) . This means as long as the NSNumber value fits in 24 bits (for iPhone/iPad's 32-bit ARM processors), no actual instance will be created. So in theory, if you are sure that the values will never overflow 24 bits, you can actually do just myNumber == #5.
Which is not really a good advice. Stick to [myNumber integerValue] == 5. Tagged pointers are there to help the runtime, not the programmers.
NSNumber *yourNumber = #(5)
Use when yourNumber should never be nil. it will crash when yourNumber becomes nil
[myNumber isEqualToNumber:yourNumber]
Use when yourNumber can be nil
myNumber.integerValue == yourNumber.integerValue
note that you have to be aware of the max value yourNumber can take.
If yourNumber will exceed INT_MAX 2147483647, use longValue or longlongValue

Resources