the super keyword tells the compiler to search for method in the superclass of the class where the method is firstly defined. For example, if Class Father defines a new method called X which contains [super init], then I use method X in Class Son, the compiler would search for init method in Class Grandfather, since X is firstly defined in Class Father.
My question is, what if there exists a inherited method of NSObject that has not changed? For example, what if I use "init" method in a subclass that contains super? Since in this case, the init method is firstly defined in NSObject itself. Would it "skip over" NSObject, or just implements whatever inside NSObject since there is no higher classes?
-(id)init
{
self=[super init];
//code continues
}
What super means is that Objective-C calls the method on the self object, but does not use the implementation of the method in the same class as the one where super was called. Slightly confusing, but consider this:
#interface Grandparent : NSObject
- (void)a;
- (void)b;
#end
#implementation Grandparent
- (void)a {NSLog(#"Grandparent a");}
- (void)b {NSLog(#"Grandparent b");}
#end
#interface Parent : Grandparent
#end
#implementation Parent
- (void)a {NSLog(#"Parent a"); [super a];}
#end
#interface Offspring : Parent
- (void)a {NSLog(#"Offspring a"); [super a];}
- (void)b {NSLog(#"Offspring b"); [super b];}
#end
Look at the implementation of -[Offspring b]. It calls [super b], but Parent doesn't provide an implementation of -b. Objective-C will carry on looking up the hierarchy until it finds -[Grandparent b], and executes that. If Grandparent didn't have the method it would look on NSObject.
Now consider calling -[Offspring a]. That logs a message, then calls [super a], which is the implementation on Parent. That message in turn calls [super a]—because it does it in a method defined on Parent, this will start looking for -[Grandparent a] (even though the instance is actually an Offspring object).
What all this means is that for any NSObject descendent, calling a message via super has the possibility of ending up at the NSObject implementation of the method. That's not the same as saying that super always reaches NSObject, because there are classes in Objective-C that don't derive from NSObject (such as NSProxy).
You're right. Let's say neither Father nor Grandfather override init, but Son does, using the implementation in your snippet. If you do [[aSon alloc] init], the call to [super init] would look for Grandfather's implementation of init, which wouldn't be found, and then it would get to NSObject's implementation, which would then be executed.
Related
I am working on a code that I am not sure to understand well. I have a class ChildA that inherits from MotherA.
In a ChildA, I do :
MotherA *theObj ;
[theObj method1];
[theObj method2];
theObj recognizes method1, but not method2, whereas both are defined in ChildA, I don't understand why. (Tell me if my question is not clear).
Thank you !
If theObj is an instance of MotherA then it responds only to those methods defined on MotherA.
If it is an instance of ChildA then it responds to the union of those methods defined on MotherA and ChildA. Anywhere that the two define the same method, calls to that instance will go to the implementation from ChildA. The implementation of ChildA can internally defer to the MotherA implementation by calling super.
This code doesn't make much sense:
MotherA *theObj ; [theObj method1]; [theObj method2];
Shouldn't that be [super method1]? Also theObj == nil so nothing will happen anyway.
You cannot call [super method2] if method2 is only defined in the subclass (ChildA) and not the superclass (MotherA).
If you have a class...
#interface MotherA: NSObject
- (void)method1;
#end
and a subclass...
#interface ChildA: MotherA
- (void)method2;
#end
Then you can only call method2 on instances of ChildA. method1 can be called on instances of ChildA and MotherA.
A subclass inherits the methods from its superclass. Not the other way around.
What you are doing (I think) is expecting a superclass to inherit the methods of its subclass.
Being new to objectiveC I was experimenting with the super keyword. I wanted to know if the super keyword is only used for calling the base class method.
Consider the following code
#interface foo_base : NSObject
{
int int_ivar;
}
-(void) base_method;
-(void) shared_method;
#end
#interface foo_der : foo_base
-(void) der_method;
-(void) shared_method;
#end
In the implementation of shared_method if I try doing this
#implementation foo_der
- (void) shared_method
{
[super shared_method]; //Works ok call base class method
int_ivar =23; //Works ok (Access base class ivar)
self->int_ivar = 23; //Works ok (Access base class ivar)
super->int_ivar=23; //Error- Why ? is super only limited to methods?
}
...
...
#end
Is the super keyword only used for calling the base class methods from the derived class ?
Yes, super is only for invoking methods. super is not really an object pointer. If it were, it would have the same pointer value as self. They refer to the same thing. It's just that super changes the lookup of the method implementation.
When you message self, the search for the implementation for the message you sent begins in the actual class of the object pointed to by self. That can be different than the static type of the self pointer.
When you message super, the search begins in the superclass of the class in whose implementation the message-to-super statement appears. That's the only purpose of super.
I'm very new with objective-C, and I am trying to implement a 'factory' style class extending a cocos2D class called CCSprite.
I want there to be a class Monster which extends CCSprite that will create and issue sprites with unique IDs so that I may call and manipulate the generated sprites later on; I am extending CCSprite to add an instance_id property and a static global_id variable.
I want to be able to use all the CCSprite constructors such as spriteWithImageNamed along with all the other functions CCSprite has, the problem is that my instance_id isn't being assigned/incremented properly(It's always 0), and I don't really know where to start to fix this.
Here is what I have:
Monster.h:
#import "CCSprite.h"
#interface Monster : CCSprite
#property (nonatomic, readonly) int instance_id;
#end
Monster.m:
#import "Monster.h"
static int global_id = 0;
#implementation Monster:CCSprite
#synthesize instance_id;
-(id) init{
self = [super init];
if(self){
instance_id = global_id;
global_id++;
}
return self;
}
#end
Is the init function called every time any constructor is called? (eg spriteWithImageNamed)?
How do I ensure my extended properties and variables are applied when I call the parent class's functions?
In Objective-C, a class's designated initializer is the one that other initializers are supposed to call into. For many classes, this is indeed init. According to the Cocos2D documentation, however, init is not the designated initializer for CCSprite. Instead, the designated initializer is:
- (id)initWithTexture:(CCTexture *)texture rect:(CGRect)rect rotated:(BOOL)rotated
So that is the method you need to override if you want all the other initializers to call your version.
(If you want to confirm for yourself that this works, you can put a log statement or breakpoint in your implementation of the designated initializer, then call other initializers and make sure the designated initializer is called.)
Your init method may or not be called depending on what init these factory methods are using internally. For example, initWithFile may call self = [super init] instead of self = [self init], in which case your implementation will not be called. You can ensure that your initialization is always performed by overriding all init methods of all superclasses and do your necessary setup. Also keep in mind that you can also override any of the factory methods, where you can get the object from calling super, and then assign it's id property before returning.
looking at CCSprite it seems that every factory method like +(id)spriteWith* calls an initWith* method
and every initWith* method at the end of the chain calls [self init]
so if you override the init method as you do in monster.m you are ok!
by the way, if you want to create your sprite using a factory method do not call [CCSprite spriteWith*], call [Monster spriteWith*] instead
best regards
I have two classes, named Parent and Child, as below. Parent is the superclass of Child I can call a method of the superclass from its subclass by using the keyword super. Is it possible to call a method of subclass from its superclass?
Child.h
#import <Foundation/Foundation.h>
#import "Parent.h"
#interface Child : Parent {
}
- (void) methodOfChild;
#end
Child.m
#import "Child.h"
#implementation Child
- (void) methodOfChild {
NSLog(#"I'm child");
}
#end
Parent.h:
#import <Foundation/Foundation.h>
#interface Parent : NSObject {
}
- (void) methodOfParent;
#end
Parent.m:
#import "Parent.h"
#implementation Parent
- (void) methodOfParent {
//How to call Child's methodOfChild here?
}
#end
Import "Parent.h" in app delegate's .m file header.
App delegate's application:didFinishLaunchingWithOptions: method..
Parent *parent = [ [Parent alloc] init];
[parent methodOfParent];
[parent release];
You can, as Objective C method dispatch is all dynamic. Just call it with [self methodOfChild], which will probably generate a compiler warning (which you can silence by casting self to id).
But, for the love of goodness, don't do it. Parents are supposed to provide for their children, not the children for their parents. A parent knowing about a sub-classes new methods is a huge design issue, creating a strong coupling the wrong way up the inheritance chain. If the parent needs it, why isn't it a method on the parent?
Technically you can do it. But I suggest you to alter your design. You can declare a protocol and make your child class adopt that protocol. Then you can have to check whether the child adopts that protocol from the super class and call the method from the super class.
You could use this:
Parent.m
#import "Parent.h"
#implementation Parent
- (void) methodOfChild {
// this should be override by child classes
NSAssert(NO, #"This is an abstract method and should be overridden");
}
#end
The parent knows about the child and child has a choice on how to implement the function.
super means "invoke a method dispatching on the parent class", so can use super in the subclass because a subclass only has one parent class. A class can have many _sub_classes though, so how would you know which method implementation to call, in the general case? (Hence there is no such thing as a sub keyword.)
However, in your example you have two separate methods. There's nothing stopping you (assuming you have very good reasons for doing something like this!) from saying, in the parent,
- (void) methodOfParent {
[self methodOfChild];
}
if your super has multiple subs then go for this one for the specific
sub's method
if ([super isKindOfClass:[specificsub class]]) {
[specificsub methodName];
}
if your super is dealing with that object (that sub) so sub's method
loggedin will be called an other way is in you super class
super *some = [[sub alloc] init];
[some methodName];
This can be done by over riding the method in subclass. That is create a method in parent class and over ride the same in subclass.
Does calling [super init] do the same thing in a category as a subclass? If not, what's the difference?
In order to understand this, it's probably important to understand the way an object is stored during runtime. There is a class object1, which holds all the method implementations, and separately, there is a structure with the storage for the instance's variables. All instances of a class share the one class object.
When you call a method on an instance, the compiler turns that into a call to objc_msgSend; the method implementation is looked up in the class object, and then run with the instance as an argument.
A reference to super takes effect at compile time, not run time. When you write [super someMethod], the compiler turns that into a call to objc_msgSendSuper instead of the usual objc_msgSend. This starts looking for the method implementation in the superclass's class object, rather than the instance's class object.2
A category simply adds methods to the class object; it has little or no relation to subclassing.
Given all that, if you refer to super inside of a category, it does indeed do the same thing that it would inside of a class -- the method implementation is looked up on the class object of the superclass, and then run with that instance as an argument.
Itai's post answers the question more directly, but in code:
#interface Sooper : NSObject {}
- (void) meth;
#end
#interface Sooper ()
- (void) catMeth;
#end
#interface Subb : Sooper {}
- (void) subbMeth;
#end
#interface Subb ()
- (void) catSubbMeth;
#end
#implementation Sooper
- (void) meth {
[super doIt]; // Looks up doIt in NSObject class object
}
- (void) catMeth {
[super doIt]; // Looks up doIt in NSObject class object
}
#end
#implementation Subb
- (void) subbMeth {
[super doIt]; // Looks up doIt in Sooper class object
}
- (void) catSubbMeth {
[super doIt]; // Looks up doIt in Sooper class object
}
#end
1 See Greg Parker's writeup [objc explain]: Classes and meta-classes
2One important thing to note is that the method doesn't get called on an instance of the superclass. This is where that separation of methods and data comes in. The method still gets called on the same instance in which [super someMethod] was written, i.e., an instance of the subclass, using that instance's data; it just uses the superclass's implementation of the method.
So a call to [super class] goes to the superclass object, finds the implementation of the method named class, and calls it on the instance, transforming it into the equivalent of [self theSuperclassImplementationOfTheMethodNamedClass]. Since all that method does is return the class of the instance on which it was called, you don't get the superclass's class, you get the class of self. Due to that, calling class is kind of a poor test of this phenomenon.
This whole answer completely ignores the message-passing/method call distinction. This is an important feature of ObjC, but I think that it would probably just muddy an already awkward explanation.
No, they do different things. Imagine a class structure like this: NSObject => MyObject => MySubclass, and say you have a category on MyObject called MyCategory.
Now, calling from MyCategory is akin to calling from MyObject, and therefore super points to NSObject, and calling [super init] invokes NSObject's -init method. However, calling from the subclass, super points to MyObject, so initializing using super invokes MyObject's -init method, which, unless it isn't overridden, behaves differently from NSObject's.
These two behaviors are different, so be careful when initializing using categories; categories are not subclasses, but rather additions to the current class.
Given the below example, super will call UIView init (not UINavigationBar init method)
#implementation UINavigationBar (ShadowBar)
- (void)drawRect:(CGRect)rect {
//draw the shadow ui nav bar
[super init];
}
#end
If you subclass it, [super init] will call UINavigationBar init method.
So yes, if there are additional things you will do in UINavigationBar init (extra from UIView) they do different things.
Edit: the following is built on a flawed premise, please look at josh's answer.
not deleting, still an interesting reference for something that could potentially lead you astray.
They are the same thing... without referencing any outside dicussions we may have had where you stated that I should ..."answer an academic question with an academic answer"
#implementation categoryTestViewController (ShadowBar)
- (void)viewDidAppear:(BOOL)animated {
//draw the shadow ui nav bar
NSLog(#"super's class = %#, self's class %#",[super class],[self class]);
if ([self class] == [super class]) {
NSLog(#"yeah they are the same");
}
}
#end
outputs:
2011-05-29 08:06:16.198 categoryTest[9833:207] super's class = categoryTestViewController, self's class categoryTestViewController
2011-05-29 08:06:16.201 categoryTest[9833:207] yeah they are the same
and calling the [super viewDidAppear:] will result in calling nothing... not a loop, so I don't know what it is really doing there.