iPhone - Use of self = [super init] when [super init] fails - iphone

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

Related

Swift - Calendar Component Extracting Seconds - Memory [duplicate]

On page 17 of this WWDC14 presentation, it says
Working with Objective-C? Still have to manage autorelease pools
autoreleasepool { /* code */ }
What does that mean? Does it mean that if my code base doesn't have any Objective-C files, autoreleasepool {} is unnecessary?
In an answer of a related question, there is an example where autoreleasepool can be useful:
- (void)useALoadOfNumbers {
for (int j = 0; j < 10000; ++j) {
#autoreleasepool {
for (int i = 0; i < 10000; ++i) {
NSNumber *number = [NSNumber numberWithInt:(i+j)];
NSLog(#"number = %p", number);
}
}
}
}
If the code above gets translated into Swift with autoreleasepool dropped, will Swift be smart enough to know that the number variable should be released after the first } (like some other languages does)?
The autoreleasepool pattern is used in Swift when returning autorelease objects (created by either your Objective-C code or using Cocoa classes). The autorelease pattern in Swift functions much like it does in Objective-C. For example, consider this Swift rendition of your method (instantiating NSImage/UIImage objects):
func useManyImages() {
let filename = pathForResourceInBundle
for _ in 0 ..< 5 {
autoreleasepool {
for _ in 0 ..< 1000 {
let image = NSImage(contentsOfFile: filename)
}
}
}
}
If you run this in Instruments, you'll see an allocations graph with 5 small hills (because outer for-loop), like the following:
But if you do it without the autorelease pool, you'll see that peak memory usage is higher:
The autoreleasepool allows you to explicitly manage when autorelease objects are deallocated in Swift, just like you were able to in Objective-C.
Note: When dealing with Swift native objects, you generally will not receive autorelease objects. This is why the presentation mentioned the caveat about only needing this when "working with Objective-C", though I wish Apple was more clear on this point. But if you're dealing with Objective-C objects (including Cocoa classes), they may be autorelease objects, in which case this Swift rendition of the Objective-C #autoreleasepool pattern is still useful.
If you would use it in the equivalent Objective-C code, then you would use it in Swift.
will Swift be smart enough to know that the number variable should be
released after the first }
Only if Objective-C does. Both operate along the Cocoa memory management rules.
Of course ARC knows that number goes out of scope at the end of that iteration of the loop, and if it retained it, it will release it there. However, that does not tell you whether the object was autoreleased, because -[NSNumber numberWithInt:] may or may not have returned an autoreleased instance. There is no way you can know, because you don't have access to the source of -[NSNumber numberWithInt:].
#autoreleasepool can be used in Objective-C and Swift code to guarantee working with Objective-C code which relies on autorelease
[Under the hood]

Objective C init method and inheritance

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.

When decoding an object from NSCoder, what's the best way to abort?

I'm decoding a custom object from a cached serialization. I've versioned++ the object since it was encoded, and if the serialized version is an old version I want to just throw it away. I was under the impression that I could just return nil from my initWithCoder: method and all would be well. That's throwing an error though.
EDIT - Per NSObject's +version: documentation "The version number applies to NSArchiver/NSUnarchiver, but not to NSKeyedArchiver/NSKeyedUnarchiver. A keyed archiver does not encode class version numbers.". I'm using a keyed archiver so that approach won't help.
I am using my own embedded version number, not the NSObject version + NSCoder's versionForClassName:. Is that the way to go about this? Will NSCoder automatically abort attempting to decode if the Class versions don't match, or do I need to do this check manually.
I'm currently doing this:
- (id)initWithCoder:(NSCoder *)coder {
if (![coder containsValueForKey:#"Class.User.version"]) {
// Version information missing
self = nil;
return nil;
}
NSInteger modelVersion = [coder decodeIntForKey:#"Class.User.version"];
if (modelVersion < USER_MODEL_VERSION) {
// Old user model, discard
self = nil;
return nil;
}
...
}
and I'm getting this error when it tries to decode old versions
-[__NSPlaceholderDictionary initWithObjects:forKeys:]: number of objects (0) not equal to number of keys (46)'
This all seems odd because initializers can fail for any number of reasons, and the pattern is to return nil in that case, so should this really crash a decoder?
EDIT - Per Apple's documentation it sounds like I'm approaching this the right way. There does not appear to be, however, a mechanism for completely aborting decoding if an object is old and out of date. I don't have or want an upgrade path from the old object, so what do I do?
instead of setting self to nil, set all of the individual properties that you decode to nil, then return self. So, write this:
if (modelVersion < USER_MODEL_VERSION) {
// Old user model, discard
object1 = nil;
object2 = nil;
...
object46 = nil;
}
return self;
This will essentially return an object of the right type and all, but all properties will be nil.

About init question

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.

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.