Is `super` local variable? - objective-c

// A : Parent
#implementation A
-(id) init
{
// change self here then return it
}
#end A
A *a = [[A alloc] init];
a. Just wondering, if self is a local variable or global? If it's local then what is the point of self = [super init] in init? I can successfully define some local variable and use like this, why would I need to assign it to self.
-(id) init
{
id tmp = [super init];
if(tmp != nil) {
//do stuff
}
return tmp;
}
b. If [super init] returns some other object instance and I have to overwrite self then I will not be able to access A's methods any more, since it will be completely new object? Am I right?
c. super and self pointing to the same memory and the major difference between them is method lookup order. Am I right?
sorry, don't have Mac to try, learning theory as for now...

Dreamlax's answer is correct... but, clarification may be helpful.
a. Just wondering, if self is a local
variable or global? If it's local then
what is the point of self = [super
init] in init? I can successfully
define some local variable and use
like this, why would I need to assign
it to self.
self is not a local variable. It is an argument to the method call. The first argument, in fact. The second argument is _cmd, the name of the selector of the method being executed.
What is special about self is that self is used by the compiler to access instance variables. That is, if you say self = [super init] and the superclass's init happens to return something different, any further instance variable accesses will still be correct.
b. If [super init] returns some other
object instance and I have to
overwrite self then I will not be able
to access A's methods any more, since
it will be completely new object? Am I
right?
If super's init returns an instance of something that is incompatible with A, then something has gone horribly awry in the design of the superclass. Keep in mind that Objective-C is fully dynamic. Thus, there is no reason that whatever is returned by super's init actually needs to be an instance of A, but it better had damned well act like an A. Now, it could be a completely new instance of a subclass of A and, thus, all of the methods of A will work just fine.
Reading between the lines; remember that Objective-C is fully dynamic. There is no such thing as static method dispatch. The class of an object could change at any time and any random method call will still work as long as the new class responds to the method. Not that this actually happens at runtime, just that it could.
c. super and self pointing to the same
memory and the major difference
between them is method lookup order.
Am I right?
Now, this is the fun question. super doesn't really point to anything. For all intents and purposes, super can be treated as the one bit of magic in this. That is, when the compiler sees super as the target of a method call, it compiles it as a slightly different call site that calls through to one of the variants of objc_msgSendSuper() which -- as name implies -- effectively "searches" for the method's implementation starting in the parent class of the class within which the call was compiled.

Self is an argument provided to the method implementation. All Objective-C instance and class methods have two implicit arguments that precede the method's arguments. The implicit arguments are self and _cmd. _cmd is the selector used to determine the method implementation.
If super returns an instance of a different class, then that would be the case, but it is also possible that it may return a different instance of the same class.
super is a keyword, not a variable. It informs the compiler to use a different runtime function that begins method resolution at one class higher than the current class.

Related

objective-c init

Is it really necessary to call init after object allocation? I mean: in Java for instance if you don't call it (the constructor), it will be called anyway (the default constructor). In Objective C you can create an alternative constructor (like Java) and one of the things I see the most is self = [super init]. I read: cocoawithlove article, but in the end it's not clear why we should make such assignment self = [super init]. It just says that the [super init] can return a different object, and then we must replace the self with that new object. That wouldn't explain why we do it in first place.
is it really necessary to call init after object allocation?
Yes, it is necessary. To compare to Java, calling [super init] (or some other designated initializer) effectively runs the superclass' constructor. This mechanism is provided for you in Java, but not in Objective-C. So it is not called implicitly in ObjC as it is in Java. In ObjC, you must call one of the superclass' designated initializers explicitly. If you do not call your superclass' initializer, your object will not be entirely initialized. This may result in no exhibited side-effects, or it could result in a completely unusable object which invokes undefined behavior.
why we should make such assignment self = [super init].
Right, alloc creates an allocation (with zeroed memory) and sets the pointer to the class information isa, but a superclass' initializer permits the superclass to exchange the instance with another which may be more appropriate. Typically, you would avoid doing this in your own subclasses. The other reason to perform this and the nil check is that it is the means an error is handled. In ObjC, exceptions are generally non-recoverable, so the conventional way to report an error to the subclass is to return nil. That is why it is also important not only to assign self, but also to test it for nil.
There is a big difference between Objective C and Java. Java is an interpreted language built from the ground up, while Objective C was built on top of C language. Anything that you can do in C is valid in Objective C as well.
Unlike Java, C (and by extension, Objective C) has "raw memory allocation". In Java, a call to new automatically calls the constructor. The language makes it impossible to go around this mechanism. In Objective C, however, you can allocate raw memory. The memory does not contain a ready-to-use object at the time it is returned to you from alloc - it is only partially prepared for use. The memory block has a reference count, and it provides enough space to fit your object, but it is not ready to receive messages that your own subclass has implemented. That is why you must call init (or use new, which combines alloc and init for you).
The if (self = [super init]) assignment/check lets you trap errors during the construction phase of your object. If your own initializer fails, you can set self = nil to report the problem up the chain. Assigning a different object is far less common, but it could be done as well.
After allocation instance variables should be instantiated.
self = [super init] refers to initialize the super class init method
A common mistake is to write
self = [[super alloc] init];
which returns an instance of the superclass, which is NOT what you want in a subclass constructor/init. You get back an object that does not respond to the subclass methods, which can be confusing, and generate confusing errors about not reponding to methods or identifiers not found, etc.
self = [super init]
is needed if the super class has members (variables or other objects) to initialize first before setting up the subclasses' members. Otherwise the objc runtime initializes them all to 0 or to nil. (unlike ANSI C, which often allocates chunks of memory without clearing them at all)
And yes, base class initialization can fail because of out-of-memory errors, missing components, resource acquisition failures, etc. so a check for nil is wise, and takes less than a few milliseconds.

iOS - Cannot use 'super' as a reference?

I'm trying to use an NSInvocation to invoke a superclass method from the subclass. The code involved is relatively straightforward, it goes like:
- (NSInvocation*) invocationWithSelector:(SEL)selector {
NSInvocation* call = [[NSInvocation alloc] init];
[call retainArguments];
call.target = super; //ERROR: use of undeclared identifier 'super'
call.selector = #selector(selector);
return call;
}
This seems a bit odd to me, as I had always assumed that super followed pretty much the same rules as self (i.e. it could be treated as a direct reference to the object in question and assigned to variables, used as a return value, etc.). It appears that this is not the case in practice.
Anyhow, is there any simple way to get my NSInvocation to target the superclass implementation (I cannot use self as the target, because the subclass overrides the superclass methods), or do I need to look for some other approach?
See What exactly is super in Objective-C? for more info, but super is not actually an object. Super is a keyword for the compiler to generate obj-c runtime calls (specifically objc_msgSendSuper). Basically, it is like casting your class to its superclass before sending it the message.
EDIT So if you've overriden the method you want to call, you are going to have to write another method to call [super method] directly and set your invocation to call that method instead. The runtime will only send messages to objects, and they will be dealt with at the lowest member of the inheritance chain that implements them.
super and self are the same object, so just use
call.target = self;
The difference between them is that you can use super as a receiver of a message to self as a way to ensure that it invokes the superclass' implementation.

Objective-C initializers and overriding self

I have a question about writing your own init methods in objective-c. I've read a few different books and have seen a couple of ways to do it but the consensus is the right way to do it is like this:
- (id)init
{
self = [super init];
if(self!=nil)
{
}
return self;
}
I'm a little confused about the line "self = [super init]". My understanding is that, there's no guarantee that [super init] will return the class that you expect it to. I think this is called "class clusters". But in the normal case, where it does return the class you expect it to, if I set self to point to a class that is returned to me, aren't I just saying that self is referring to an object of a different class rather than the class that I'm in the init method of?
To summarize, why set self to be the superclass vs the actual class I'm in?
From a blog I read:
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...
"The assignment has no effect on self" is what confuses me. Why does it have no effect? If I set something = to something else, shouldn't that have an effect?
There are different opinions on the proper way to write -init methods. There are two reasons that would make you think that self = [super init] is a good idea. (The assignment itself isn't anything special; Objective-C considers self to be a hidden parameter of the method, and you can reassign to parameters. The changed self only applies for the remainder of the method.)
Superclass -init returns instance of different class
As you suggested, some classes use the "class cluster" pattern. However, in the most common implementation of this pattern, it's the -alloc method on the base class that is likely to return an instance of a different class, and it's all the -init... methods on the placeholder class that are likely to return an instance of a different class. self = [super init] is not useful here.
Superclass -init returns a different instance of the same class
This is the reason that self = [super init] is recommended. Some classes have logic that allows -init to return a different instance than the one that it was called on. For example, some singleton classes in the Cocoa framework do this. But in almost every case, you need to know this behavior of the superclass in order to properly subclass it. Here's an argument by Wil Shipley that self = [super init] isn't actually very useful, because either [super init] returns self anyway, or the class you're subclassing is sufficiently complicated that reassigning self and then continuing with the initialization won't work anyway.
To summarize, why set self to be the superclass vs the actual class I'm in?
This is the Apple suggested way to do things, specifically due to the case of class clusters, as you say.
In general, you should not worry about the fact that self might be of a different class in the "normal" case.
self simply identifies the object you are, not the class (the class is actually a different object in the runtime). If you think of OO inheritance properties, it is at the same time an object of its class and of its superclass (if it is clear what I am trying to say). There is no contradiction in the "normal" case, since the value of self does not change.
Also, you can think of self as a special pointer to your object. In the cluster case, self can change, that is the reason why it can happen that its class change.
Hope this helps clarifying things. You will also find an interesting reading in this article by Wil Shipley.

Method Overloading in Objective-C - not used for init?

I have just started programming in Objective-C, I understand it only partially supports method overloading due to the way the method names are generated (see this question).
However, my question is why I have never seen it used in any examples. The code below seems to work fine, but any sort of example I have seen, the second init would be named initWithServerName or something like that, instead of taking advantage of the overloading.
-(id) init {
self = [super init];
return self;
}
// usually this would be called initWithName or something? but to me it
// seems easier this way because it reminds me of method overloading from C#.
-(id) init: (NSString*)newServerName {
self = [super init];
if(self) {
serverName = [[NSString alloc] initWithString:newServerName];
}
return self;
}
What is the reason for this? Does it cause problems in sub-classes to name methods this way?
Unlike Algol-style languages like C#, Objective-C's syntax is specifically designed for literate method names. init: tells me nothing about the method parameter. Is the receiver initing the thing I'm passing? No. It's using the argument in some way, so we use a descriptive name like initWithFormat: to specify that the argument is a format string.
Also, Objective-C does not have method overloading at all. Period. A single selector for a given class can only have one type signature. The only way to change behavior based on an argument's class is to have a method take a generic type that could include many different classes (like id or NSObject*), ask the argument for its class and do different things depending on the result of that query.
That's not the same method. In objective-C a selector named init is different than one named init:. The colon is part of the selector name.
Also, init is overridden fairly often, you just have the wrong method.
Aside from jer's answer, it also does not allow you to specify multiple ways to initialise an instance. For example, NSString has initWithString:, initWithFormat:, etc.

is it good form to release self in an init method when that method allocates and returns something else?

In my code, I have something that looks like this:
#implementation MyClass
- (id) initWithType:(NSInteger)type {
[self release];
if (type == 0) {
self = [[MyClassSubclass1 alloc] init];
} else {
self = [[MyClassSubclass2 alloc] init];
}
return self;
}
//...
#end
which I think handles any potential memory leaks. However, I have seen code out there that does something similar, except it doesn't release self before reassigning it to another newly allocated instance. Is it not necessary to release self here or is the other code I've seen incorrect?
Your code looks technically correct, from a memory management perspective. Replacing self with a different alloc'd object loses the pointer to the original object, and nobody else will be able to release it, which would cause a leak. Try commenting out the release call and run it with Leaks in Instruments.
Just be cautious about opening this particular can of worms — Foundation.framework (part of Cocoa) uses class clusters for collections and strings, but doing so is a fairly advanced concept. A better approach might be to have a class method for each subclass, using the AbstractFactory pattern.
In any case, determining the subclass type based on an integer is a bad idea — any change in mapping from type to class will break dependent code. If you're going that way, why not just pass in the class object itself?
This looks like poor use of object-oriented design.
If you're creating a different instance depending on a type variable, then why don't you have subclasses for those types?
It would be much cleaner to define a base class with all the common functionality, and a subclass for each "type" variation.
What does the class do? We might be able to point you in the right direction.
Code-wise, your example code is correct, but it's generally bad practice to replace the instance with a different instance. Unless the init method is a factory method re-using instances or a singleton initializer, avoid releasing self en-lieu of another instance.