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.
Related
Assume the following Objective-C class:
#interface Appliance : NSObject
{
NSString *productName;
int voltage;
}
#end
What are the pros and cons of implementing init method A instead of B?
A) -(id)initWithName:(NSString *)name;
B) -(Appliance *)initWithName:(NSString *)name;
I see they both work in XCode, i.e. they both will result in a valid Appliance instance. "A" seems to be the standard among books I've read and codebases I've looked at, and I'm wondering why this is.
Point in fact, for quite some time the best practice return type from a class initializer (in Objective-C) is instancetype instead of id.
Oh, reopen. :-)
Indeed, you did not ask for the difference id vs. instancetype. And for -init… the answer to this non-asked Q would be easy: There is no difference, because the compiler converts id to instancetype silently.
You asked for id vs. CustomClass*. And you get a completely different answer from me: With CustomClass* a subclass had to cast the result of the superclass' designated initializer. Let's have an example:
#interface BaseClass : NSObject
- (BaseClass*)initWithWhatever; // Typed to class, designated initializer
#end
#implementation BaseClass
- (BaseClass*)initWithWhatever // Typed to class
{
self = [super init]; // What's the return type of -init (NSObject)?
…
}
#end
#interface Subclass : BaseClass
// First problem: I like it to announce in the interface, that a class overwrites
// a method of the base class. Doing so I would have to change the return type. Ugly.
// If I do not redeclare -initWithWhatever it is inherited from BaseClass, still
// having BaseClass* as the return type. Is that the truth? Really?
// However, I do not overwrite it here, but have a new initializer.
- (Subclass*)initWithSomethingElse;
#end
#implementation Subclass
- (Subclass*)initWithSomethingElse
{
// Second Problem:
// First, I have to execute the superclass' designated initializer
self = [super initWithWhatever];
// Wait a minute!
// self is a reference to Subclass. The return value of -initWithWhatever has the type
// BaseClass*. So I assign a reference of the base class to a reference of the subclass:
// Compiler error, false positive. The code is correct.
// So I would have to cast. Ugly, ugly, ugly.
#end
…
// Third problem:
Subclass *object = [[Subclass alloc] initWithWhatever];
// Typing -initWithWhatever to BaseClass* would lead to a compiler error here again.
// Compiler error, false positive. The code is correct.
To make the long story short: Without a mass of castings it would be impossible to type initializers to the concrete class.
I am currently in the process of Learning Objective-C coming from a c++ background. I wanted to know if there was a way for the derived class method that has the same signature as the base class to not override it.
I read that every method in Objective-C is virtual.Are there any ways in which a derived class does not override the base class method be simulated ?
I have the following two classes
#interface foo_base : NSObject
-(void) base_method;
-(void) shared_method;
#end
#interface foo_der : foo_base
-(void) der_method;
-(void) shared_method;
#end
and when I use it like this
foo_base *b = [[foo_der alloc]init];
[b shared_method]; //Derived class method will be called.
I know I could do something like this inside the derived class method
[super shared_method] //call base class method
No, in Objective-C if a derived class implements a base class method with the same signature, it is always overridden. You could create a "helper" function like this if you really want to access the base class method directly.
- (void) callSuperSharedMethod {
[super sharedMethod];
}
#interface hello:SKScene
#end
#implementation hello
+(void)method{
[self here];
}
#end
main.m
[hello method];
here,when i call this class method without allocating memory for object then method self,belong to whom????
my question is self belong to class that contain the method calling on then because i did not define object then ,why i still can use self on this?????
is it still belong to class on which it calling ??please give me proper concept of self on instance method and class method.
When you refer to self in class method, self refers to the class, itself, not to a particular instance of that class. By using self rather than the actual class name in these class methods, it makes it easier to subclass the class in question. Specifically, the class method will be inherited, but the self references will now refer to the subclass rather than the base class. If you refered to the base class by name, this would not be true.
Consider this factory method:
#implementation BaseClassObject
// THIS IS WRONG
+ (BaseClassObject *)object {
return [[BaseClassObject alloc] init];
}
#end
And consider this subclass:
#interface SubClassObject : BaseClassObject
#end
Then consider code that does:
SubClassObject *object = [SubClassObject object]; // WRONG
The problem is that the object factory method will return a BaseClassObject rather than a SubClassObject. But that is remedied if we alter the definition of that factory class method to use self:
#implementation BaseClassObject
// THIS IS RIGHT
+ (instancetype)object {
return [[self alloc] init];
}
#end
Now when I refer to [SubClassObject object], I'll get an instance of SubClassObject rather than BaseClassObject.
Note: In my contrived example, it's rather important to use self. You may, though, encounter code where it does not appear to be immediately relevant (for example, you might have a base class, but no subclass at this time).
Even in this case, you probably should be in the habit of using self in these class methods, regardless, to "future-proof" your code, so that if you ever subclass this base class at some unforeseen date in the future, these class methods are more likely to function properly even when subclassed.
Just to add a little to Rob's answer: the class object is created automatically by the compiler and/or Objective-C runtime. (It doesn't matter to you which it is.) For all intents and purposes, it's permanent. There's no need for it to be managed.
I have an interesting problem where I am trying to call class methods on an class which I essentially know nothing about in my test method. I can inspect its inheritance and any protocols it may implement but can't see an easy way to just call a method on it without getting tied up with an NSInvocation. The code below, albeit crudely, tries to demonstrate the problem I am having.
#interface ClassA : NSObject
+ (Class)classIsPartialClassOf;
#end
#implementation ClassA
+ (Class)classIsPartialClassOf {
return [NSString class];
}
#end
#interface ClassB : NSObject
#end
#implementation ClassB
- (id)init {
[ClassB testClass:[ClassA class]];
}
+ (void)testClass:(Class)classDecl {
/* obviously if you know the type you can just call the method */
[ClassA classIsPartialClassOf];
/* but in my instance I do not know the type, obviously there are no classmethods to perform selector such as the fictional one below */
[classDecl performSelector:#selector(classIsPartialClassOf)];
}
#end
Methods for getting implementations seem to return instance variants and I can't get them to fire on the static class itself.
Are my options limited to invocations or have I missed something obvious and should kick myself?
Thank you in advance for your help.
What is the problem? Your code
[classDecl performSelector:#selector(classIsPartialClassOf)];
should work. As will (simpler to write)
[classDecl classIsPartialClassOf];
Class objects are objects. And class methods are simply methods called on a class object.
"Methods for getting implementations seem to return instance variants and I can't get them to fire on the static class itself."
Then use objc_getMetaClass("ClassName") instead of objc_getClass. Class objects are objects themselves and are instances of their metaclass. If you pass the metaclass object to e. g. class_getMethod(), everything will be fine.
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.