Official Forum for Programming in Objective-C (the iPhone Programming Language) - Stephen Kochan
June 25, 2017, 12:03:33 PM *
Welcome, Guest. Please login or register.
Did you miss your activation email?

Login with username, password and session length
   Home   Help Search Login Register Chat  
Pages: [1]   Go Down
Author Topic: What does it mean when you assign [super init] to self?  (Read 3540 times)
Posts: 8

« on: September 28, 2010, 07:13:00 AM »

This is an explanation I found on Matt Galagher blog which help me a lot to understand the whole concept of using [super init]. Please revide it, if something isn't correct:

One of the strangest pieces of common syntax in Objective-C is the line self = [super init];. Without any explanation, this arrangement raises a few questions. Does this line set the self value for the instance? Is self  just a variable like any other? If so, why have it at all? I'll address each of these questions and show how the compiler converts uses of self and method invocations.

Converting a method invocation

The first step to understanding the self parameter is to look at how the compiler converts a standard method invocation.

When you type the following:

Code: (Objective-C)
MyClass *myObject = [[MyClass alloc] initWithString:@"someString"];

The compiler converts this into function calls that look like this:

Code: (Objective-C)
class myClass = objc_getClass("MyClass");
SEL allocSelector = @selector(alloc);
MyClass *myObject1 = objc_msgSend(myClass, allocSelector);

SEL initSelector = @selector(initWithString:);
MyClass *myObject2 = objc_msgSend(myObject1, initSelector, @"someString");

The compiler has slightly more efficient means of getting the class and SEL values but if you look at assembly code, you will see objc_msgSend calls for every method invocation.

So what is "self"?
Every method that you declare has two hidden parameters: self and _cmd.

The following method:
Code: (Objective-C)
- (id)initWithString:(NSString *)aString;

is converted by the compiler to the following function call:
Code: (Objective-C)
id initWithString(id self, SEL _cmd, NSString *aString);

The reality is that self is simply a hidden parameter on every method. Like any other parameter, it receives its value from the function invocation.

Yes, _cmd is also a hidden parameter on every method that you can access if you choose. In reality, there are few uses for the _cmd parameter except in obscure cases.

You can experiment with this by eliminating objc_msgSend and invoking the function for a method directly. Instead of calling:
Code: (Objective-C)
[myObject someMethodWithParameter:someValue];
You can reach your method implementation directly by recreating the work done by objc_msgSend.
Code: (Objective-C)
SEL methodSelector = @selector(someMethodWithParameter:);
IMP someMethodFunction = class_getMethodImplementation([myObject class], methodSelector);
someMethodFunction(myObject, methodSelector, someValue);
The only reason why self has a value on the inside of the someMethodWithParameter: implementation is because the pointer myObject is passed as the first parameter into someMethodFunction. If you pass a different value as this first parameter, then self will have a different value on the inside of the method.

If you pass a value of a different class, you have a good chance of crashing the program. The following section explains why.

Why have a "self" parameter at all?
A method needs to know what data to act upon. The self parameter tells the class the data to act upon and so is essential to object oriented programming.

This statement may seem a little strange, since you can easily implement a method without using the self parameter by name. The reality is that the compiler uses the self parameter to resolve any reference to an instance variable inside a method.

If you had a class defined like this:
Code: (Objective-C)
@interface MyClass : NSObject
    NSInteger value;
- (void)setValueToZero;
then the method:
Code: (Objective-C)
- (void)setValueToZero
    value = 0;
is converted by the compiler into:
Code: (Objective-C)
void setValueToZero(id self, SEL _cmd)
    self->value = 0;
So self is essential for accessing any instance variables, even if you never literally type "self".

So does self already have a value when init is called?
If you remember back at the start, I said that the initWithString: part of a typical [[MyClass alloc] initWithString:@"someString"] invocation is converted into an objc_msgSend call:
Code: (Objective-C)
MyClass *myObject2 = objc_msgSend(myObject1, initSelector, @"someString");
So by the time we get to the inside of the method, self already has a value; its value is myObject1 (i.e. the allocated object, as returned from the [MyClass alloc] call. This is essential because without it, the super invocation wouldn't be possible the self value is used by the compiler to send the invocation:
Code: (Objective-C)
[super init];
Code: (Objective-C)
objc_msgSendSuper(self, @selector(init));
Yes, self already has a value when your initializer starts. In fact, it is almost guaranteed to be the correct, final value.

So why assign the value returned from [super init] to self?
Looking at a typical initializer method:
Code: (Objective-C)
- (id)initWithString:(NSString *)aString
    self = [super init];
    if (self)
        instanceString = [aString retain];
    return self;
Why do we assign [super init] to self here?

The textbook reason is because [super init] is permitted to do one of three things:

   1. Return its own receiver (the self pointer doesn't change) with inherited instance values initialized.
   2. Return a different object with inherited instance values initialized.
   3. Return nil, indicating failure.

In the first case, the assignment has no effect on self and the instanceString is set on in the original object (the line instanceString = [aString retain]; could have been the first line of the method and the result would be the same).

In the third case, the initialization has failed. self is set to nil, no further action is taken and nil is returned.

The rationale for assigning to self is associated with the second case: if the returned object is different, we want the:
Code: (Objective-C)
        instanceString = [aString retain];
which gets converted to
Code: (Objective-C)
        self->instanceString = [aString retain];
to act on the correct value, so we have to change the value of self to point to this new object.

It's almost never required to initialize self
So the rationale for assigning to self is that the [super init] could return a different object and should initialize that different object instead of the old (likely invalid) object.

The question to consider is then: when would [super init] return a different object?

The answer is that it will return different objects in one of the following situations:

    * Singleton object (always returns the singleton instead of any subsequent allocation)
    * Other unique objects ([NSNumber numberWithInteger:0] always returns the global "zero" object)
    * Class clusters substitute private subclasses when you initialize an instance of the superclass.
    * Classes which choose to reallocate the same (or compatible) class based on parameters passed into the initializer.

In all but the final case, continuing to initialize the returned object if it changes is a mistake the returned object is already completely initialized and isn't necessary related to your class anymore.

So the list of three things that [super init] is permitted to return can now be expanded to four by splitting the "Return a different object" point into two:

   1. Return its own receiver (the self pointer doesn't change) with inherited instance values initialized.
   2. Return an object of the same class, requiring further initialization.
   3. Return a different object that is already completely initialized.
   4. Return nil, indicating failure.

In this list, we now have two cases (2 and 3) which are incompatible. The typical "assign [super init] to self" initializer handles cases 1, 2 and 4.

An init approach to handle cases 1, 3 and 4 would actually be:
Code: (Objective-C)
- (id)initWithString:(NSString *)aString
    id result = [super init];
    if (self == result)
        instanceString = [aString retain];
    return result;
So class clusters, singletons and unique objects all use case 3, putting dozens of Cocoa classes in this category. I'm only aware of NSManagedObject that uses case 2. 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.


Don't give up!
Hero Member
Posts: 3114

« Reply #1 on: November 14, 2010, 12:36:13 PM »

Thanks for posting this.  It's extremely detailed and certainly useful for those who want to know what's going on "under the hood."


Steve Kochan
Posts: 12

« Reply #2 on: December 20, 2010, 08:26:04 AM »

Thanks for posting.  That certainly helped me understand what is going on.  I recommend making this post a sticky.

Pages: [1]   Go Up
Jump to:  

Powered by MySQL Powered by PHP Powered by SMF 1.1.11 | SMF © 2006-2009, Simple Machines LLC Valid XHTML 1.0! Valid CSS!
Entire forum contents ゥ 2009 All rights reserved.