in objective-c, is if(self) { [self initFOO] } redundant? - objective-c

I am studying from the Stanford CS193P from Paul Hegarty. Then I see something like this, which I also see other people using:
- (id)init
{
self = [super init];
if (self) {
[self someSetupFunctions];
}
return self;
}
Isn't that redundant? Why not just use:
- (id)init
{
self = [super init];
[self someSetupFunctions];
return self;
}
I thought that messages to nil just fizzle, so why not use this second format?

You may want to do other things than passing messages to self. For example:
- (id)init
{
self = [super init];
if (self) {
// set up ivars
_anIvar = 5;
}
return self;
}

What's really meant by [self someSetupFunctions] is some initialization code, not just a single method call, and a number of important things will fail if self is nil. Most notably, attempting to assign initial values to instance variables will crash.

The self = [super init]; if (self) test is because some initialisers will return different objects, including nil if it's impossible to create an object that satisfies the condition required by the initialiser.
In your own initialiser you need to defend against the superclass doing that, because if you try to access an instance variable on nil you're definitely doing the wrong thing (whether it crashes as a result or not: it usually will).

Related

Xcode 5.1 changes -(id) to -(instancetype) when using built-in init snippet. Why?

How come Apple changed the built-in init method snippet from:
- (id)init
{
self = [super init];
if (self) {
}
return self;
}
to:
- (instancetype)init
{
self = [super init];
if (self) {
}
return self;
}
?
instancetype is the best return type for Objective-C init methods. If you write id instead of instancetype in an init method then the compiler will assume you really meant instancetype (at least in ARC mode).

Overriding init method to allocate memory for instance variable object

I am attempting to override the init method of a class so that initializing the object also initializes the ivar object:
-(id)init
{
if (self = [super init])
{
someIVarObject = [SomeClass alloc] init];
}
return self;
}
1) Would this code even work, and even so, is this solution discouraged?
2) Why is the if condition not == ? Is it an assignment? If so, why the if?
Sorry if this is obvious! >_<
Yes, it would work, and afaik it's still the way it should be done.
What it does is to call [super init] and allows it to do one of three things;
Return self (ie the same object self is already set to.
Return an entirely new object.
Return nil for failure.
The result is then assigned to self, which makes sure that the rest of the constructor operates on the correct object in case it changed. The if is there to catch the case where [super init] returned nil, in which case the rest of the constructor should be skipped.
1) Here you are declaring a local variable someIVarObject. You should have declared this within the interface or implementation of your class in curly braces, and should then assign it as someIvarObject = .... An example:
#implementation MyClass {
SomeClass *someIvarObject;
}
- (id)init
{
if(self = [super init])
{
someIvarObject = [[SomeClass alloc] init];
}
return self;
}
#end
2) It is an assignment. There is a long history behind this idiom but it mostly comes down to handling the possibility that [super init] returns a different object than init was originally invoked upon.
This...
if(self = [super init])
will work, but it will give you a compiler warning (unless you've turned off this warning).
You can also suppress the warning by using double parenthesis:
if((self = [super init]))
My current preference:
self = [super init];
if(self)
You've got some typos, unbalanced brackets, and the thing you say is an ivar is not an ivar (you declare it inside the if, which makes its scope local to that block. You want to put instance variables in the {}s after your #implementation or #interface declarations). But yes, this is generally how this would work.
However, I'd take a hard look at whether you really need an ivar or not. I can't remember the last time I used one in my code. 99% of the situations I used to use them in, a #property is now a much better solution.
As an added benefit, #propertys synthesize their own getters and setters, (usually) obviating the need to write manual allocing boilerplate, thus making this question moot.
1) this code will work but this line:
SomeClass *someIVarObject = [SomeClass alloc] init];
makes a little sense. Declare SomeClass *someIVarObject in .h file and initialize it in init like this:
someIVarObject = [SomeClass alloc] init];
2) this line if (self = [super init]) is equivalent to:
self = [super init]; if (self != nil)
i.e. it ensures that init method of the base class has returned a proper value

Assignment of self on if condition-Objective C

self is a hidden instance variable that points to the current object:
- (id) initWithAmount:(double)theAmount forBudget:(Budget *)aBudget{
if(self = [super init]){
budget = aBudget;
amount = theAmount;
}
return self;
}
is it similar to
this.budget=super.aBudget;
this.amount=super.theAmount;
Normally:
if(control)//true or false
{
//stmts will get executed
}
but instead of returning a true value here the self is assigned with super. Why is it so?
Is it similar to constructor. How to use constructors(default,parameterised and copy) in objective C?
Because an assignment expression also returns the result of the assignment,
if (self = [super init]) { ...
is equivalent to:
self = [super init];
if (self) { ...
And since if does not just test pure boolean values but treats everything that is non-zero, non-NULL or non-nil as true, the if statement tests if self is nil after the assignment.
When we write our own init methods, we are supposed to assign [super init] to self because a init method is free to return a different object than the receiver of the method. If we just called [super init] without assigning it to self, we might initialize a different object than self, which is clearly not desirable.
This form is just the short form of the following code:
- (id)initWithAmount:(double)theAmount forBudget:(Budget *)aBudget {
self = [super init]; // you must do this, because every object inherits all properties
// from the parent class, and they must be initialized
// init can return nil if there was an error during the initialization
// so if the parents initialization failed, there is no point of initializing
// the child object
if (self != nil) {
budget = aBudget;
amount = theAmount;
}
return self;
}
It's the way initializers are defined in Objective-C. It's part of the two-stage creation pattern: seperating memory allocation and initialization of the object. As self references the object instance where the initializer resides in it has to go up the inheritance tree and set what they return to itself, after that it's your turn.
The call of super is only used in the designated initializer. You could have more initializers which should always use the designated initializer.
The pattern
- (id)init {
self = [super init];
if (self) {
// code
}
return self;
}
Is the correct way to handle failures. The test will fail if self = [super init]; returns nil: An error occured in initializers up the inheritance tree, so you don't want to use a failed initialization and skip your own code.

Doesn't the standard object initialization in Objective-C lead to memory leaks?

The standard way to create an object in Objective-C looks like this:
MyClass* object = [[MyClass alloc] init];
The standard implementation of MyClass's init method would look something like this:
-(id) init
{
self = [super init];
if(self) { /* initialize */ }
return self;
}
Aside from some syntax changes, and excluding factory methods, that seems to be the recommended way to write an init method, and to use it.
As I understand it, the purpose of self = [super init]; is to handle the case where [super init] fails. But if it does fail, and returns nil, wouldn't there be a memory leak? The reason being that MyClass's init will return nil, object will be nil, there will be no more pointers that reference the object allocated with [MyClass alloc], and therefore no way to release it.
These are the two solutions I can think of are, but I haven't seen either one in regular practice.
After a call to alloc, check the results before calling init:
MyClass* object = [MyClass alloc];
if(object == nil) { /*handle the error */ }
else { object = [object init]; }
Or, if [super init] fails, release the memory. Something like this:
-(id) init
{
id temp = [super init];
if(!temp) { [self release]; }
self = temp;
if(self) { /* initialize */ }
return self;
}
Am I wrong in that reasoning? It could be argued that [super init] is unlikely to fail, but then why assign the results of it to self and check for nil? I'd be happy to see some clarification.
If [super init] wants to return nil, it should also call release on self.
An init method should get rid of the object if it decides to abort and return nil.
However, that's just what the if (self) covers. The self = [super init] serves another purpose: An init method is allowed to return something other than the object that the message was sent to. For a real-life example, [NSArray alloc] returns a dummy object, and that object's various init… methods return the real array.

Should I always release self for failed init methods?

Should I always release self when there is a failure inside init, or should I only do so if I have initialized instance variables first?
To put it another way, is this pattern valid? Is there a time when I shouldn't release self inside an init method, or should I assume that if the control flow enters init, self has at least a retain count of 1?
- (id)init
{
if ((self = [super init]) == nil)
{
[self release];
return nil;
}
//do some init stuff
if (somethingFailed)
{
[self release];
return nil;
}
return self;
}
If some check you need in your initialization method fails, then yes you should release self. Note however, that if [super init] returns nil it does not make sense to send release to self as self is nil. This is actually frowned on by Apple:
You should only call [self release] at the point of failure. If you get nil back from an invocation of the superclass’s initializer, you should not also call release.
Example:
- (id)init
{
self = [super init];
if(self) {
// do some init stuff
if (somethingFailed)
{
[self release]
self = nil;
}
}
return self;
}
Also see the Mac Dev Center documentation on Handling Initialization Failure