This question already has answers here:
Should you use 'isEqual' or '=='?
(2 answers)
Closed 8 years ago.
How is comparing operator '==' for NSObject?
Method -isEqual: works fine for me, but when I'm using -isEqual I need to check if objects exists. With '==' I don't need to check this but where I can find documentation for it?
From Apple documentation:
Returns a Boolean value that indicates whether the receiver and a
given object are equal. (required) This method defines what it means
for instances to be equal. For example, a container object might
define two containers as equal if their corresponding objects all
respond YES to an isEqual: request. See the NSData, NSDictionary,
NSArray, and NSString class specifications for examples of the use of
this method. If two objects are equal, they must have the same hash
value. This last point is particularly important if you define
isEqual: in a subclass and intend to put instances of that subclass
into a collection. Make sure you also define hash in your subclass.
if you do like this
if([obj1 isEqual:obj2])
and obj1, or obj2 is nil then you will get NO. (if this is what you meant by your question)
- Now
if(obj1 == obj2)
This is a pointer comparison. Pointers
The == operator tests whether the two expressions are the same pointer to the same object. Cocoa calls this relation “identical”
To test whether two objects are equal, you would send one of them an isEqual:
Related
When we write:
"exampleString".hashCode
Is there a genral mathematical way, some algorithm that calculates it or it gets it from somewhere else in Dart laungage?
And is the hashcode of a String value still the same in another languages like java, c++...?
To answer your questions, hashcodes are Dart specific, and perhaps even execution-run specific. To see how the various hashcodes work, see the source code for any given class.
The Fine Manual says:
A hash code is a single integer which represents the state of the object that affects operator == comparisons.
All objects have hash codes. The default hash code implemented by Object represents only the identity of the object, the same way as the default operator == implementation only considers objects equal if they are identical (see identityHashCode).
If operator == is overridden to use the object state instead, the hash code must also be changed to represent that state, otherwise the object cannot be used in hash based data structures like the default Set and Map implementations.
Hash codes must be the same for objects that are equal to each other according to operator ==. The hash code of an object should only change if the object changes in a way that affects equality. There are no further requirements for the hash codes. They need not be consistent between executions of the same program and there are no distribution guarantees.
Objects that are not equal are allowed to have the same hash code. It is even technically allowed that all instances have the same hash code, but if clashes happen too often, it may reduce the efficiency of hash-based data structures like HashSet or HashMap.
If a subclass overrides hashCode, it should override the operator == operator as well to maintain consistency.
That last point is important. If two objects are considered == by whatever strategy you want, they must also always have the same hashcode. The inverse is not necessarily true.
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).
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.
If I have an array of strings, can I reliably test if it contains a given string with NSArray containsObject - or should I loop through and test isEqualToString on each object?
containsObject: uses isEqual:, which is reliable and tests for equality, i.e., if the object in the array and the parameter are really equal. It might actually call isEqualToString: under the hood.
Yes you can use containsObject method as well which is internally calls isequal method only.
Say I have a NSArray, and each item is an NSDictionary with three keys keyA, keyB, and keyC - each referring to objects of unknown type (id).
If I wanted to write a method that found the given element with those three keys i.e.
-(NSDictionary *) itemThatContainsKeys:(id)objectA and:(id)objectB and:(id)objectC
would I run into trouble by simply enumerating through and testing object equality via if([i objectForKey:(keyA) isEqualTo:objectA]) etc? I would be passing in the actual objects that were set in the dictionary initialization - ie not strings with the same value but different locations.
Is this bad practise?
Is there a better way to do this without creating a database?
You can override isEqual to stipulate the notion of equality for your type. The same rules apply as in other languages:
If you provide an implementation of equals you should provide an implementation of 'hash'
Objects that are 'equal' should have the same 'hash'
Equals should be transitive -> if A equals B, and B equals C, then C must equal A.
Equals should be bi-directional -> if A equals B, then B must equal A.
This will ensure predictable behavior in classes like NSSet, that use hash for performance, falling back to equals on when there's a collision.
As Jason Whitehorn notes, Objective-C also has the convention of providing another isEqualToMyType method for convenience.
AppCode, EqualsBuilder, Boiler-plate code
It would be nice if there was something like Apache's 'EqualsBuilder' class, but in the meantime AppCode does a fine job of implementing these methods for you.
The isEqual: method compares object identity unless overwritten by a subclass. Depending on what class the target is, this May or may not be what you want. What I prefer is to use a more class specific comparison like isEqualToNumber: simply because of it's explicitness. But, isEqual should work depending on the target.
Other than that, and not knowing more specifics of what you're doing, it's hard to say if there is a better way to accomplish what you're after. But, here are my thoughts;
An array of a dictionary almost sounds like you might need a custom class to represent some construct in your app. Perhaps the dictionary could be replaced with a custom object on which you implement an isEqualToAnotherThing: method. This should simplify your logic.