iOS - Cannot use 'super' as a reference? - objective-c

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.

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.

Objective-c using parent properties in init method

I read that using properties in init method is considered as bad practice. But should I use parent class properites ?
For example
-(id) init
{
if (self = [super init])
{
self.parentProp = someVal; // (1)
parentProp = someVal; // (2)
}
return self;
}
What is prefered (1 or 2) and why? Thanks!
After you've called super's init method, and it has returned, the superclass's part of your object is initialized and ready for use. It's normal and expected that you use its property accessors after that. For example. If you make a subclass of UIViewController, it's normal to then set your (inherited) title property, or modify your navigationItem, in your init method.
That said, you can break this behavior. If you've overridden one of your superclass's methods (including one of its accessors methods), and then you call that method in your init method, it's up to you to be sure your overridden method will behave properly before your object is fully initialized.
More subtly, maybe you're overridden a superclass method, and then you call a different superclass method that you haven't overridden. Well, what if the method you call turns around and calls the method you have overridden? You need to be aware of this possibility too.
All that said, I reiterate that it's perfectly normal to use your superclass's property accessors to customize it after you have initialized it by calling one of its init methods.
To answer your question - neither of them.
(2) is not a property access, but direct instance variable access. It depends on the class hierarchy design, but in general I would strongly discourage from using ivars in non-private interfaces - for details, see this answer to related question
In general, you should not use any of the class public methods (including properties access) in the class initializer (and in the dealloc for that matter) - if you class hierarchy doesn't prohibit subclassing explicitly. Because if you do - the subclasses overriding these methods (or properties accessors) will get them called while being in invalid state (not yet initialized or already dealloc'ed).
While I've encountered a number of problems caused by pt.2 in general it seems to be a common practice to ignore it (i.e. to use self/parent class properties in initializer). So I would say it's up to you. Either write more code for explicit setup outside of your classes initializers and feel confident that you would never encounter this problem. Or have probably more simple/short initialization and easier usage of your class but stay aware of that problem.

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.

Why send a class through +(Class)class before calling a class method?

I've been browsing Sketch, an example program they ship with Xcode, and I keep seeing things like this (not always though):
[[MyClass class] classMethod]
Now, since MyClass isn't an instance of the class, I would just do:
[MyClass classMethod]
As an experiment though, I was able to make a difference between the two above statements, by overriding + (Class)class and returning another class! If you ever want to do this, you would need the former version for it to work, so I can see that there could be a usage for this, but is there really?
It sounds like an awful idea tampering like this with +class, but please enlighten me if there is. Thanks!
To see some examples, check out the method -copy: in SKTGraphicsView.m in the Sketch program found in Developer/Examples/Sketch/.
In the case that you cited, I don't really see any good reason for calling +class. However, there is definitely a case for calling [[self class] doSomething] instead of [[MyClass class] doSomething]. In the first case, [self class] can return the correct result if the object has been subclassed. In the second case, you will always get the MyClass class object, which means that MySubClass could not effectively override the +doSomething class method.
There is no good reason for using +class in that case. +class simply returns the object (the class object) that it is called on, same as -self. In most cases, people use [SomeClass class] to get the class object for a class, because the name of a class is not an expression, and can only be used as the receiver in the message calling syntax as a special case. Therefore, in all other contexts, they use [SomeClass class] to get the class object as an expression (although [SomeClass self] would also work equally well).
In the case you showed, it is redundant, because it is the receiver of a message. I am guessing they did it out of habit or ignorance (they thought that you must always write [SomeClass class] to use the class).

Is `super` local variable?

// 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.