Moshe
Moshe

Reputation: 58087

Is calling super in a category the same as calling it in a subclass?

Does calling [super init] do the same thing in a category as a subclass? If not, what's the difference?

Upvotes: 12

Views: 3974

Answers (4)

jscs
jscs

Reputation: 64002

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.

Upvotes: 14

Grady Player
Grady Player

Reputation: 14549

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.

Upvotes: 0

Rad'Val
Rad'Val

Reputation: 9231

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.

Upvotes: 2

Itai Ferber
Itai Ferber

Reputation: 29843

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.

Upvotes: 8

Related Questions