I stumbled upon this post In Objective-C why should I check if self = [super init] is not nil?
I can understand this syntax:
- (id)initWithString:(NSString *)aString
{
self = [super init];
if (self)
{
instanceString = [aString retain];
}
return self;
}
or this syntax:
- (id)init;
{
if (!(self = [super init]))
return nil;
// other stuff
return self;
}
but I still don't understand the "standard" template syntax
- init {
if((self = [super init])) {
// set up instance variables and whatever else here
}
return self;
}
Can someone tell as clearly as possible what (3) does more or less compared to (1) or (2) ? All I read is so confusing (why people can't agree with something which is purely technical seems like politics :))
Nevertheless as I read authors article, and as I can fuzzily understand it goes far beyond just syntactic sugar debate or matter of taste. For example it is said:
Curiously then, while case 3 is overwhelmingly more common, initializers that support 1, 2 and 4 but are incompatible with case 3 have become the standard.Curiously then, while case 3 is overwhelmingly more common, initializers that support 1, 2 and 4 but are incompatible with case 3 have become the standard.
So I'd like to have a deep philosophical answer from Objective C Gurus if possible.
The key to understanding (3) is the if line
if((self = [super init])) {
In C, every operator has a return value, you just don't have to use it. So just as 3 + 4 returns 7, the operator = returns the same value that was just assigned. That allows you to do interesting things like this with it:
int a, b, c, d;
a = b = c = d = 5;
(This works because the operator has right-to-left associativity. This means that a = b = 3;is equivalent to a = (b = 3); so b is set to three first, then a is set to that same value.)
So now we can observe that the test
if((self = [super init])) {
is exactly the same as
self = [super init];
if (self) {
They all do exactly the same thing, though. As for the preferred option, a lot of people think that putting expressions into if statements is bad practice, as it's hard to notice if you're not used to it. Apple do seem to have settled with the first example you listed in their templates, and have also added a compiler warning for the first, if you try to do it without the stupid double brackets if(()).
All of the three accomplish the exact same task: call [super init], assign it to self (in case super's init returns a different object, which in some cases is possible) and test whether that returned pointer is nil. If it is nil, leave without doing anything else.
Examples #1 and #3 are identical, they just have the syntax slightly rearranged. #2 is different in that it returns an explicit value of nil, while #1 and #3 do so implicitly.
Which on to use is a matter of taste. I prefer #2 because it saves an indention level (I like to avoid indention if possible, because if you have indented four or five levels deep it really gets hard to follow the flow).
People do not universally agree which one to use for the very same reason they cannot agree which indention style (K&R, Allman, GNU, Whitesmith, ...) to use: it's personal preference and does not change the logic.
If you have an variable and no ==, !=, > or something like that the expression will be true if your variable (in this case a pointer to an object) is not 0. That means if your pointer don't point to nothing. nil and NULL are defined for 0 so you can write
if ((self = [super init]) != nil) {}
or
if ((self = [super init]) != NULL) {}
or
if ((self = [super init]) != 0) {}
or simply
if ((self = [super init])) {}
too.
I also prefer the extra "room" afforded by NOT using the overly verbose, rarely intuitive... and frankly ugly syntax I see in Apple's code / around town, etc...
-(id) init { if (self != super.init) return nil; ...
I may be drowning babies - and causing handsome old Indians to weep in the process - but I just stick it all on one line... and remember to return self; AOK so far!
Related
I've tried searching google and this site regarding my question but found no answer.
I'm a beginner with Obj-C and would like this question answered.
What is the benefit of using parameters in my methods.
for example..
-(id)initWithName:(NSString *)newName atFrequency:(double)newFreq {
self = [super init];
if (self != nil) {
name = newName;
frequency = newFrequency;
}
return self;
}
versus
-(void)myMethod {
self = [super init];
if (self != nil) {
name = newName;
frequency = newFrequency;
}
return self;
}
I understand that the -(void) means the method has no return type, and the -(id) means that the first method has 'id' as a return type, and 'id' is generic....
can anyone help explain? I hope my question makes sense, thank you all for your help.
Parameters are inputs to a method, just like function/method parameters in any language. In your second example, on the line frequency = newFrequency;, where is newFrequency supposed to come from?
In other languages, where you might have something like
void initWithName(string newName, double newFreq);
In Obj-C the equivalent is
- (void)initWithName:(NSString *)newName atFrequency:(double)newFreq;
The difference is that in Obj-C, there is an extra piece of the method name for each parameter (like the atFrequency) — in this case, the method name is initWithName:atFrequency:, not just initWithName:.
(This is actually optional, you only have to have a : for each parameter. Technically initWithName:: is still a valid method name, but that's not considered good practice in Obj-C.)
See also:
How do I pass multiple parameters in Objective-C?
Is there a language out there in which parameters are placed inside method name?
For aesthetic reasons, I decided to change this:
if ((self = [super init])) {
// init self
}
return self;
Into this:
if (!(self = [super init])) return nil;
// init self
return self;
In theory, they do the same thing. The first one is the classic way, simply works. Debugging the second one, I found that it almost worked. The "if" does it right, the init code also, but, after returning "self", the debugger get back to the "if" and returns nil!
All classes I made with the second one I'm reverting to use the "correct" way because they where initing with nil, but I really want to know why does it behaves like that! I'm afraid that this may be the result of something else wrong!
There's absolutely no difference between your two versions other than aesthetic preference, so something else must be going wrong. Perhaps you should post your whole init method?
I created a test class for this, with the following init method:
- (id)init
{
if (!(self = [super init])) return nil;
[self setText:#"foo"];
return self;
}
It initializes as expected, and I can access the text property. So as Nick pointed out, something else must be malfunctioning.
I'm from the C++ world so the notion of assigning this makes me shudder:
this = new Object; // Gah!
But in Objective-C there is a similar keyword, self, for which this is perfectly acceptable:
self = [super init]; // wait, what?
A lot of sample Objective-C code uses the above line in init routines. My questions:
1) Why does assignment to self make sense (answers like "because the language allows it" don't count)
2) What happens if I don't assign self in my init routine? Am I putting my instance in some kind of jeopardy?
3) When the following if statement fails, what does it mean and what should I do to recover from it:
- (id) init
{
self = [super init];
if (self)
{
self.my_foo = 42;
}
return self;
}
This is a topic that is frequently challenged by newcomers:
Wil Shipley: self = [stupid init];
Matt Gallagher: What does it mean when you assign [super init] to self?
Apple documentation: Implementing Initializers
Cocoa-Dev: self = [super init] debate
Basically, it stems from the idea that a superclass may have over-ridden the designated initializer to return a different object than the one returned from +alloc. If you didn't assign the return value of super's initializer into self, then you could potentially be dealing with a partially initialized object (because the object that super initialized isn't the same object that you're initializing).
On the whole, it's pretty rare for super to return something different, but it does happen in a couple of cases.
In Objective-C, initializers have the option of returning nil on failure or returning a completely different object than the one the initializer was called on (NSArray always does this, for example). If you don't capture the return value of init, the method might be executing in the context of a deallocated object.
Some people disagree about whether you should do the whole assign-to-self rigamarole if you don't expect to get something else back from the superclass initializer, but it's generally considered to be good defensive coding.
And yes, it looks weird.
It is true that init may return nil, if the initialization fails. But this is not the primary reason why you should assign to self when you implement your own initializers.
It has been mentioned before, but it is needed to stress even harder: the instance returned from an initializer may not be the same instance as the one you sent in, in fact it may not even be of the same class!
Some classes use this as a standard, for example all initializer to NSString and NSArray will always return a new instance of a different class. Initializers to UIColor will frequently return a different instance of a specialized class.
And you yourself can happely implement something like this if you want:
-(id)initWithName:(NSString*)name;
{
if ([name isEqualToString:#"Elvis"]) {
[self release];
self = [[TheKing alloc] init];
} else if (self = [super init]){
self.name = name;
}
return self;
}
This allows you to break out the implementation of some special case into a separate class, without requiring the clients of your API to care or even know about it.
All the other points here are valid, but it's important for you to understand as well that self is an implicit parameter to every Objective-C method (objc_msgSend() passes it) and can be written to, just like any other method parameter. (Writing to explicit parameters is generally frowned upon, unless they are out parameters.)
Typically, this is only done in the -init method, for the reasons others have stated. It only has any effect because self is returned from the method and used in the assignment id obj = [[NSObject alloc] init]; It also affects the implicit resolution of ivars, because, for example, if myVar is an ivar of my class, then accessing it in a method causes it to be implicitly resolved to self->myVar.
I'm still new to Objective C, but this post helped me in understanding this.
To sum it up, most init calls return the same object that self is already initialized to. If there is an error, then init will return nil. Also, some objects such as singletons or unique objects (like NSNumber 0) will return a different object than the one initialized (the singleton or a global 0 object). In these situations you need to have self reference that object. I'm by no means an expert in what is going on behind the scenes here, but it makes sense on the surface, to me.
If [super init] returns nil that means that you have been deallocated and your self parameter is now an invalid pointer. By blindly following the self = [super init] convention you will save you from potentially nasty bugs.
Consider the following non-typical initializer:
- (id)initWithParam:(id)param {
if (!param) {
// Bad param. Abort
self = [super init]; // What if [super init] returns nil?
[self release];
return nil;
}
else
{
// initialize with param.
...
}
}
Now what happens if my superclass decides to abort and return nil? I have been de-allocated and my self parameter is now invalid and [self release] will crash. By re-assigning self, I avoid that crash.
Why does the Clang Static Analyzer (CSA) output the following message:
Although the value stored to 'self' is
used in the enclosing expression, the
value is never actually read from
'self'
for the following method:
- (id)init
{
return (self = [super initWithStyle:UITableViewStyleGrouped]);
}
The code works as expected, so I'm wondering whether the code is incorrect from a technical point-of-view, this is a bug within CSA or I'm simply missing something very obvious.
FYI, I'm using this pattern because I don't want the class creating an instance of this class to be able to specify the table style.
A more "proper" way to do this would be as follows:
- (id)init
{
self = [super initWithStyle:UITableViewStyleGrouped];
return self;
}
And it should satisfy the static analyzer
edit:
My best guess as to why Clang doesn't like that line:
When you write (self = [super initWithStyle:UITableViewStyleGrouped]), the result of the init call is stored into a temporary variable, which is then copied into self, and then it is that temporary variable that is actually returned from the method.
Although this is perfectly legal and normal behaviour (and will not break your app), the static analyzer (correctly) notices that the value stored in self is never actually read.
To illustrate, the following code:
- (id)init
{
id temp = [super initWithStyle:UITableViewStyleGrouped];
self = temp;
return temp;
}
Throws the same static analyzer error.
It's telling you that the self = part is unnecessary. It's not incorrect in sense of "broken or dangerous," but in that it's pointless. The variable self is just never used, so there's no point in assigning to it. It could be simply written as return [super initWithStyle:UITableViewStyleGrouped]; without any problem.
I've read in many places that you should always initialize Objective-C objects like so:
- (id) init {
if (self = [super init]) {
....
}
return self;
}
Because the super's init method may return a separate object from the current self.
Now I'm trying to do something like this, and I'm not sure if I have it right, vis-a-vis how retaining and releasing should work:
- (id) init:(int)idx {
id obj = [Cache findSelf:idx];
if (obj) {
[self release];
self = [obj retain];
} else {
self = [self doLoad];
}
return self;
}
I'm mostly curious if this is the correct way to do the retaining and releasing of self and obj. Is there a better way?
You're correct about the self = [super init] part, since some Cocoa classes actually do return a different object than the one that was allocated. However, this is the exception rather than the rule, and doing so in your own code should be exceedingly rare or not done at all. Although it may be tempting to intercept -init calls, you'd be going against the grain of established convention and what Objective-C programmers expect the code to do.
This type of -init method is generally a bad approach, since -init methods should be as straightforward as possible, and should really be concerned with initializing the object. I'd probably write a convenience method like this:
+ (id) instanceForIndex:(NSUInteger)index {
id obj = [Cache findSelf:index];
if (obj == nil) {
obj = [[self alloc] init];
// Add to cache
}
return [[object retain] autorelease];
}
Then call this method instead of -init. This will make the -init logic much cleaner.
Also, I'm not sure what your Cache class does, but it could be worth rethinking that implementation, and using a hidden static variable to store instances (for example, an NSMutableDictionary, where the key is an NSNumber created from the index). This SO question may be of use.
I agree with Quinn that you should use a convenience class method. Still, I think that your init method is mostly correct, except in your else clause you need to call the parent initializer, i.e. self = [super init].