Testing object equality, i.e. the same physical address? - iphone

I am checking if an object I am getting back from the NSURLConnectionDataDelegate is the same object that I originally created. What I have been doing is:
// TESTING TO SEE IF THE RETURNED OBJECT IS THE SAME ONE I CREATED
if(connection == [self connectionPartial]) {
But was just curious is this is the same as doing:
if([connection isEqual:[self connectionPartial]]) {

It's not the same.
if(connection == [self connectionPartial]) {
This compares the address of the objects, eg. if the pointers point to the same instance.
if([connection isEqual:[self connectionPartial]]) {
This compares the contents of the objects. For instance for two separate NSString instances, this will return YES as long as the string content is the same:
NSString *s1 = #"Something";
NSString *s2 = #"Something";
BOOL sameInstances = (s1 == s2); // will be false, since they are separate objects.
BOOL sameContent = [s1 isEqual:s2]; // will be true, because they both are "Something"

The first snippet compares the values of the pointers themselves, just as if they were any primitive type like an int. If the addresses are the same, the expression will evaluate true.
The second sends the message isEqual: to one of the connection instances. Any class can override isEqual: to define "equality" with another instance. It's entirely possible for a class's implementation of isEqual: to be:
- (BOOL)isEqual: (id)obj
{
return arc4random_uniform(2) ? YES: NO;
}
So, no, for almost all classes they are not equivalent. (NSObject, which has the "default" implementation of isEqual:, uses the objects' hashes, which, again by default, are their addresses.)
It sounds like using the equality operator, ==, is correct in your case.

Related

Core Data Boolean property NSNumber doesn't remember it's boolean

I have a model with a property that looks like this:
When I set its value, for example:
model.isResolved = #YES;
The NSNumber that's kept inside the model "forgets" that it's a boolean:
NSLog(#"%#", strcmp([self.isResolved objCType], #encode(BOOL)) == 0 ? #"equal" : #"different");
Prints "different". What is up with that?
What is up with that?
From the documentation:
Note that number objects do not necessarily preserve the type they are created with.
That's another inconsistency-for-optimization in Cocoa.
Core Data dynamically generates getter and setter methods for all attributes (and relationships) of managed object classes. These accessor methods are different from the "usual" #synthesized accessor methods which are backed up by an instance variable.
In particular, if you set an attribute and then retrieve the attributes value again, you can get an object that is different from the "original" object. The following test shows this, foo1 is an instance of a Core Data entity with the Boolean attribute "show":
NSNumber *yes = #YES;
NSLog(#"yes = %p, type = %s", yes, [yes objCType]);
foo1.show = yes;
NSNumber *val = foo1.show;
NSLog(#"val = %p, type = %s", val, [val objCType]);
Output:
yes = 0x16e595c, type = c
val = 0x744c150, type = i
So even if you set the attribute to a c = char encoded number, the getter method returns a i = int encoded number.
This test was done in the iOS Simulator. Interestingly the same test, running on OS X 64-bit, returns a c = char encoded number.
Therefore, the actual encoding of Boolean and other scalar attributes in Core Data objects should probably be treated as an implementation detail of Core Data.
If you need to check the Core Data type as defined in the model, you can use the objects entity description instead of objCType:
NSEntityDescription *entity = [foo1 entity];
NSAttributeDescription *attr = [[entity attributesByName] objectForKey:#"show"];
NSAttributeType type = [attr attributeType];
if (type == NSBooleanAttributeType) {
NSLog(#"Its a Boolean!");
}
Its stored as a NSNumber - by the way #YES is creating a NSNumber like
[NSNumber numberWithBool:YES]
so to get the bool back out you do:
[isResolved boolValue]
(you can avoid this by ticking the Use Scalar Properties when you create your models)

NSString or NSCFString in xcode?

I m taking a NSMutabledictionary object in NSString like this :
NSString *state=[d valueForKey:#"State"];
Now sometimes state may be null and sometimes filled with text.So Im comparing it.While comparing state becomes NSString sometimes and NSCFString othertimes..So unable to get the desired result..
if([state isEqualToString#""])
{
//do something
}
else
{
//do something
}
So while comparing it is returning nil sometimes.So immediately jumping into the else block.
I need a standard way to compare if the state is empty whether it is a NSString or NSCFString ...
How can I do it?
If you're unable to get the result you want, I can assure you it's not because you get a NSCFString instead of a NSString.
In Objective-C, the framework is filled with cluster classes; that is, you see a class in the documentation, and in fact, it's just an interface. The framework has instead its own implementations of these classes. For instance, as you noted, the NSString class is often represented by the NSCFString class instead; and there are a few others, like NSConstantString and NSPathStore2, that are in fact subclasses of NSString, and that will behave just like you expect.
Your issue, from what I see...
Now sometimes state may be null and sometimes filled with text.
... is that in Objective-C, it's legal to call a method on nil. (Nil is the Objective-C concept of null in other languages like C# and Java.) However, when you do, the return value is always zeroed; so if you string is nil, any equality comparison to it made with a method will return NO, even if you compare against nil. And even then, please note that an empty string is not the same thing as nil, since nil can be seen as the absence of anything. An empty string doesn't have characters, but hey, at least it's there. nil means there's nothing.
So instead of using a method to compare state to an empty string, you probably need to check that state is not nil, using simple pointer equality.
if(state == nil)
{
//do something
}
else
{
//do something
}
You can do this
if([state isEqualToString:#""])
{
//do something
}
else
{
//do something
}
You must have to type cast it to get the correct answer.
NSString *state = (NSString *) [d valueForKey:#"State"];
if(state != nil)
{
if(state.length > 0)
{
//string contains characters
}
}

How to compare strings? (`==` returns wrong value)

I've got myself a piece of the iPhone SDK and been trying to make some simple apps. In this one, i want to compare the first character of self.label.string with the last one of ((UITextField *)sender).text. I decided to name them self.texty and self.input, respectively.
I would expect this if statement returning yes to me under certain circumstances, however I can't seem to get that done.
(in my case, my self.label.string was equal to 'hello!', while my self.input ended in an 'h'.)
self.input = [NSString stringWithFormat:#"%#", [((UITextField *)sender).text substringFromIndex:[((UITextField *)sender).text length]-1]];
self.texty = [NSString stringWithFormat:#"%#", [self.label.string substringToIndex:1]];
if (self.input == self.texty) {
return yes;
} else {
return no;
}
String comparison is not done with ==, but with one of the comparison methods of NSString.
For example:
if ([self.input compare:self.texty] == NSOrderedSame) {
if ([self.input isEqualToString:texty]) {
return yes;
} else {
return no;
}
EDIT:
Or a better version as the commenters noted:
return [self.input isEqualToString:texty];
If you're curious why the == operator doesn't work as expected, it's because you're actually comparing two scalar types (pointers to NSString objects) not the contents of the NSString objects themselves. As a result, it will return false unless the two compared NSStrings are actually the same instance in memory, regardless of the contents.

help with isEquals and hash in iphone

So i'm overriding isEquals and hash to compare custom objects to be able to remove duplicates from a NSArray. The problem is that i'm missing some values in the list which contains no duplicated items, and it seems that my hash or isEquals implementation is wrong. The custom object is a Course object which has some variables like: id and name I'll put the code here:
- (BOOL)isEqual:(id)object {
if ([object isKindOfClass:[Course self]]) {
return YES;
}
if(self == object){
return YES;
}
else {
return NO;
}
}
- (unsigned)hash {
NSString *idHash = [NSString stringWithFormat: #"%d", self._id];
return [idHash hash];
}
Then, after querying the database i put the values in an array and then in a set which should remove the duplicated items like this:
NSMutableSet *noDuplicates = [[NSMutableSet alloc] initWithArray:tempResults];
Can you see what i'm doing wrong in the isEquals or hash implementation?
Thanks a lot.
Step 1. Decide which instance variables / state are used to determine equality. It's a good idea to make sure properties exist for them (they can be private properties declared in a class extension if you like).
Step 2. Write a hash function based on those instance variables. If all the properties that count are objects, you can just xor their hashes together. You can also use C ints etc directly.
Step 3. Write isEqual: The normal pattern is probably to first test that both objects are in the class or a subclass of the method in which isEqual: is defined and then to test equality for all the properties.
So if a class Person has a name property (type NSString) and a number property (type int) which together define a unique person, hash might be:
-(NSUInteger) hash
{
return [[self name] hash] ^ [self number];
}
isEqual: might be
-(BOOL) isEqual: (id) rhs
{
BOOL ret = NO;
if ([rhs isKindOfClass: [Person class]]) // do not use [self class]
{
ret = [[self name] isEqualToString: [rhs name]] && [self number] == [rhs number];
}
return ret;
}
I don't think it is stated as an explicit requirement in the doc but it is probably assumed that equality is symmetric and transitive i.e.
[a isEqual: b] == [b isEqual: a] for all a and b
[a isEqual: b] && [b isEqual: c]implies [a isEqual: c] for all a, b, c
So you have to be careful if you override isEqual: for subclasses to make sure it works both ways round. This is also why the comment, do not use [self class] above.
Well, your isEqual: implementation really just tests if the two objects are the same class. That's not at all correct. Without knowing the details of your object, I don't know what a good implementation would look like, but it would probably follow the structure
- (BOOL)isEqual:(id)object {
if ([object isMemberOfClass:[self class]]) {
// test equality on all your important properties
// return YES if they all match
}
return NO;
}
Similarly, your hash is based on converting an int into a string and taking its hash. You could also just return the int itself as your hash.
Your code violates the principal of "objects that are equal should have equal hashes." Your hash method generates a hash from self._id and doesn't take that value into consideration when evaluating the equality of the objects.
Concepts in Objective-C Programming has a section on introspection where this topic has examples and coverage. isEqual is meant to answer the question of two objects are equivalent even if they are two distinct instances. So you want to return a BOOL indicating if the object should be considered equivalent. If you don't implement isEqual it will simply compare the pointer for equality which is not what you probably want.
- (BOOL)isEqual:(id)object {
BOOL result = NO;
if ([object isKindOfClass:[self class]]) {
result = [[self firstName] isEqualToString:[object firstName]] &&
[[self lastName] isEqualToString:[object lastName]] &&
[self age] == [object age];
}
return result;
}
From the NSObject Protocol Reference:
Returns an integer that can be used as a table address in a hash table
structure.
If two objects are equal (as determined by the isEqual: method), they
must have the same hash value. This last point is particularly
important if you define hash in a subclass and intend to put instances
of that subclass into a collection.
- (NSUInteger)hash {
NSUInteger result = 1;
NSUInteger prime = 31;
result = prime * result + [_firstName hash];
result = prime * result + [_lastName hash];
result = prime * result + _age;
return result;
}
So what defines two objects as equal is defined by the programmer and their needs. However, whatever methodology of equality is developed, equal objects should have equal hashes.
this is how you implement hash and isEqual (at-least the one which is working for me for purpose of identifying duplicates)
Hash Function
The Apple Doc says that the hash of two objects should be same for those which are considered equal( logically). hence I would implement the hash as below
-(unsigned int)hash
{
return 234;//some random constant
}
isEqual: method implemenation would be something like
-(BOOL)isEqual:(id)otherObject
{
MyClass *thisClassObj = (MyClass*)otherObject;
*// this could be replaced by any condition statement which proves logically that the two object are same even if they are two different instances*
return ([thisClassObj primaryKey] == [self primaryKey]);
}
More reference here : Techniques for implementing -hash on mutable Cocoa objects
Implementing -hash / -isEqual: / -isEqualTo...: for Objective-C collections

Why isn't my variable being assigned

Ok I think my understanding of properties in objective c may not be what I thought it was.
In my program I have a singleton that contains my class.
In my class during the init I assign a value from the singleton to my property.
I then assign a value to a property of that property.
However it does not keep the value and when I do a compare of the value in the singleton nothing has changed. What is going on here? Any ideas?
#interface MainGameLoop : NSObject {
MapData *mapData;
}
#property (retain) MapData *mapData;
-(id) init
{
self = [super init];
GlobalVariables *sharedManager = [GlobalVariables sharedManager];
self.mapData = sharedManager.mapData;
return self;
}
In a function of my class:
works:
sharedManager.mapData.currentPlayer = newCurrentPlayer;
does nothing:
self.mapData.currentPlayer == newCurrentPlayer;
self.mapData.currentPlayer == newCurrentPlayer;
Are you sure that you want two equal signs there? That statement is syntactically correct and will evaluate to either true or false.
== is a Boolean operator, while = is an assignment operator. Like what Dave said, if you are using an if statement such as if (self.mapData.currentPlayer == newCurrentPlayer) {…}, you would want to use == because it would evaluate to true or false, while = would be used to set the value of a variable, which is what I think you are trying to do.
If it's any consolation, I've made that mistake too many times to count…
Something that I do is to use NSLog() or printf() to make sure that each step is working correctly.