Are there any difference between following ways to init self?
First:
- (id)init {
self = [super init];
if (self) {
}
return self;
}
Second:
- (id)init {
if (self = [super init]) {
}
return self;
}
I like to use second way to init self. But I often see the first way when I create a new class by Xcode, it will generate those codes automatically. I am afraid the second way will make some errors in some condition which I don't know.
Nope, they are the same.
The second method is just missing out the first assignment of self
Another way you might come across is this:
- (id)init {
if (!(self = [super init])) {
return nil; // Bail!
}
// Further initialisation
return self;
}
This puts the exceptional condition in the if statement, and keeps the code that you expect to run normally (the Happy Path) closer to the left margin.
In C and most(if not all) languages these two are same.
First
a = b;
if (a) {}
Second
if (a = b) {}
First one is just using one more line.
However, they are not same as
if (a == b) {}
They’re equivalent and you can safely use either of them. Some people prefer not to use
if (self = [super init])
because it is a tad overloaded: it means ‘assign the return value of [super init] to self, and then evaluate self as a (boolean) condition’.
At first glance, one could misread that code, interpreting the assignment operator = as being the equality relational operator ==, i.e., ‘compare self to the value returned by [super init]’, which is not the case.
This can be particularly confusing for programmers that are new to C. Not every programming language allows assignments in expressions, that is, assignments must be in a separate instruction.
Both are functionally equivalent but using assignments where conditional expressions are expected is discouraged as bad programming practice because it's usually accidental, e.g:
if (myVar = 5) {
printf("The variable was 5\n");
}
When you actually meant to write:
if (myVar == 5) {
...
However the Objective-C init code is usually an exception to this because it's a well known sequence and repeated a lot, so for convenience some people may choose to write the assignment within the conditional expression.
Related
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.
What is the difference beetween :
// 1
-(id) init {
self = [super init];
if (self) {
... do init ....
}
return self;
}
// 2 - I guess it's exactly the same as previous
-(id) init {
if (self = [super init]) {
... do init ....
}
return self;
}
// 3 - is this one a really bad idea and if yes, Why ?
-(id) init {
self = [super init];
if (!self) return nil;
... do init ....
return self;
}
// 4 - I think this one sounds OK, no ? But what is returned... nil ?
-(id) init {
self = [super init];
if (!self) return self;
... do init ....
return self;
}
EDIT : Added thanks to Peter M.
// 5 - I really like the readability of this one
-(id) init {
if(!(self = [super init])) return self; // or nil
... do init ....
return self;
}
they all do the same thing but first one are commonly used because apple suggested
the second one was commonly used but it will introduce compiler warning on new version of Xcode so apple decided to change it to first one
The common one used by Apple
Does the same but the compiler does not know if you didn't mean == instead of =. You can block the warning by using another (...) around the expression, but it's not easy to read.
This one would be preferred by any good coding standards (usually not as a one-liner but with bracers). When your initialization code is long, this one significantly increase readibility.
The same as 3 but you lose some readibility. Returning nil makes the code easier to understand (maybe this is only my opinion, someone else can say 4 is better than 3).
In summary: use 1 or 3. When initialization code is long, you should use 3 to avoid having most of method code in one if block. Apple uses only 1 but don't follow it blindly. Apple has no documented coding standards and sometimes what they recommend is very questionable.
You can use 4 instead of 3 but don't mix 3 & 4 in your code.
1 is suggested simply because it's clear.
2 works, but is just bad practice. Don't embed logic in your conditionals if you don't have to.
3 makes my head hurt. Typically, you want to avoid negative checks if you can.
4 same as 3.
Both 1 and 2 are easier to read than 3 and 4, also because they are the one's used in Apple's code; many developers are used to 1 and 2.
Number 4 and 3 are the same, as if !self evaluates as true, then it means self is nil, so the statement return self is equivelant to return nil. Some people will say you shouldn't have multiple exit points (returns) in a method, but in reality having multiple return statements can cut down the number of if statements (which reduce the readability)
Number 2 has been common, however in the latest versions of xcode you will get a compiler warning if and if statement contains a single = (as many times it is a typo, when you intended to use == to compare BOOL values). To silence this warning you have to surround the statement with brackets, so instead of if (foo = bar) you would have if ((foo = bar))
But Apple must have realised that Number 2 is used a lot, and they added an exception to the rule, so you using it will now not cause a compiler warning.
As Number 2 is an exception to the rule, you shouldn't really use it. So that makes Number 1 the preferred method.
To add to the fray .. I prefer:
if(!(self = [super init]) ) return self;
Keeping it all on the one line, and no warnings in XCode 4.2 :D
I have 2 classes A and B,B is inherited from A,A is inherited from NSObject.Class A has a function -(void)init:
- (void)init {
if (self = [super init]) {
NSLog(#"A");
}
return self;
}
B has:
- (id)init {
if (self = [super init]) {
NSLog(#"B");
}
return self;
}
After compilation I have "A" and "B" in my console,despite of condition in B "if (self = [super init])" can't be true,cause its superclass init method returns void.Any ideas?
P.S. I know that init method must return id,I'm interested why this works while it shouldn't.
Listen to Chuck. He's smart :)
I'll try to add a bit more detail.
One way to think about this is "If both were declared -(id)init how would A return its value to B"?
It goes something like this. The return self statement would take the pointer to A and put it in an agreed upon location. This location would be defined in the ABI. On a OSX/x86-64 machine, it's the RAX register (IIRC). So return self really says "write the pointer to self into RAX".
Then when B regains control it knows the return value is in RAX and can use it however it likes. Note you didn't remove the return self statement from A's implementation, so self was probably still written to RAX where B found it and used it. Or maybe RAX still had a pointer from the NSObject init implementation. You'd have to disassemble it to know for sure.
Now let's say you didn't keep the return self in A's implementation. Now there's some random junk in RAX which B is going to try to use like it's a pointer to an A. This is where the undefined behavior Chuck mentions comes in. If you're lucky it will just crash.
I'm guessing your code above gave some compiler error/warning that you had to suppress?
You're invoking undefined behavior by assigning the result of a void function call. Undefined behavior means pretty much anything can happen. This falls under the header of "anything," so it can happen.
As for why the compiler isn't noticing your mistake: You probably neglected to declare the override in your header, so it's assuming that your method has the same signature as the nearest declaration it can find, NSObject's.
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
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.