I have two Objective-C classes and one is derived from the other as
#interface DerivedClass : BaseClass
{
}
The code section below belongs to BaseClass:
- (id)init {
if (self = [super init]) {
[self configure];
}
return self;
}
- (void) configure{} //this is an empty method
And the code section belongs to the DerivedClass:
-(void) configure{
NSLog(#"derived configure called");
}
Now, when I say derivedInstance = [DerivedClass new]; and watch the call stack, I see that the configure method of my derived class gets called at the [self configure] line of the base's init method.
I'm an Objective-C noob and I'm confused about how a method of a derived class gets called from the method of a base class. "self" keyword is explained to be the same thing as "this" keyword of some languages but I think this explanation is not completely correct, right?
[self someMessage] will send the message "someMessage" to the current object, which is an instance of DerivedClass.
Message dispatch is done dynamically at run-time, so it will behave as whatever the object is at that time.
Related
I'm trying to avoid duplicate code in various -(id)init flavors of a class, i.e. init, initWithFrame, initWithCoder, etc. by defining a private method that is named commonConstruct.
This method does the heavy lifting common to all init method flavors and gets called by the init constructor.
The issue i have now is that in derived classes, using the same naming convection for the initializer helper ("commonConstruct") the base class will call the derived class's commonConstruct, though it is invisible, i.e. declared in the .m file, not in the .h file.
However, the runtime finds the overloaded commonConstruct and executes that instead of its own member function.
Is there any other way than using a different name for the initializer helper in each subclass ?
In other words: Is there a way of making Objective-C member functions that are "non-virtual", i.e. don't have late (runtime) but compile-time binding?
There's no good compiler-enforced way to do this. Methods are always “virtual” and there is no enforcement of “private” methods.
The common solution is to embed the class name in the method name, thus:
#implementation Thing
- (instancetype)init {
if (self = [super init]) {
[self Thing_commonInit];
}
return self;
}
- (instancetype)initWithArg:(NSObject *)arg {
if (self = [super init]) {
[self Thing_commonInit];
[self doSomethingWithArg:arg];
}
return self;
}
- (void)Thing_commonInit {
...
}
In addition to Rob Mayoff's solution (which I use as well) you could use a static C function:
#implementation Thing
{
id _privateIvar;
}
- (id)init
{
return commonInit([super init], nil);
}
- (id)initWithArgument:(id)argument
{
return commonInit([super init], argument);
}
static Thing *commonInit(Thing *self, id argument)
{
if (self == nil)
return nil;
self->_privateIvar = argument;
return self;
}
#end
Static functions don't emit symbols, so there are no possible conflicts. You can name all of your common initializers commonInit.
Assume there is an object that initialises like so
- (void)doInit
{
NSLog(#"In BaseClass init");
}
- (id)init
{
self = [super init];
[self doInit];
return self;
}
and it has a subclass which is inited in a similar way
- (void)doInit
{
NSLog (#"In SubClass init");
}
- (id)init
{
self = [super init];
[self doInit];
return self;
}
Now if I create an instance of child class then I receive the following output:
In SubClass init
In SubClass init
when really, what I meant to happen is
In BaseClass init
In SubClass init
Is there a way to mark doInit to say that it shouldn't be overridden or do I need to create a unique name for all methods in a subclass?
I'm not entirely sure how I haven't come across this issue before, but there you go.
Edit:
I understand why this is happening, I hadn't expected that the base class would be able to call the overridden function.
I also can't just call [super doInit]; from the Subclass method because the BaseClass still needs to call doInit so that creating an instance of BaseClass will still work. If I called [super doInit], I'd still end up getting SubClass's doInit called twice.
It appears the answer is no and I'll just have to uniquely name each doInit like doBaseClassInit and doSubClassInit.
If you have a method that you don't want to by dynamically bound (i.e. don't want a subclass method to be called if it exists), you need to do it as a C function instead. So, you could do this instead:
In the base class:
static void DoInit(BaseClass *self)
{
NSLog(#"In BaseClass init");
}
- (id)init
{
self = [super init];
if (self) {
DoInit(self);
}
return self;
}
in the subclass:
static void DoInit(SubClass *self)
{
NSLog(#"In SubClass init");
}
- (id)init
{
self = [super init];
if (self) {
DoInit(self);
}
return self;
}
Note that both the DoInit methods are marked as static, so they are only visible each compilation unit (.m file) and don't conflict with each other.
You could, perhaps, try something like this in your base class. It would mean any time the init implementation inside BaseClass executed, the doInit implementation for BaseClass would be called.
- (void)doInit
{
NSLog(#"In BaseClass init");
}
- (id)init
{
self = [super init];
Class baseClass = [BaseClass class];
SEL selector = #selector(doInit);
IMP baseClassImplementation = class_getInstanceMethod(baseClass, selector);
baseClassImplementation(self, selector);
return self;
}
As I mentioned in my comment, if that's the narrowness of your need this should work as it gets around the dynamic method lookup involved with sending a message. Hope this helps!
EDIT:
Disclaimer - if you're in this situation it's probably not a good sign for the longevity of your design. This technique will get you up and running for now but please document it carefully, and consider ways to refactor your code so this is no longer used. Consider fixes like these to really be used only when extremely urgent.
The reason why you are not getting the "In BaseClass init" console message is because your subclass is not calling the super's implementation of doInit.
If you don't want doInit overridden the 'best' way to avoid doing so is to not publish the existence of this method. Remove it from your header and uniquely name the method so that a collision is unlikely. For example, many of the private methods in Apple's frameworks have a leading underscore. So, for example, you could call your method _doInit and it will be very unlikely that a subclass accidentally create it's own overiding implementation.
Nope, there's no enforceable way to prevent a subclass from overriding a method. The best you can do is to avoid putting it in the public header file for the class so someone is not likely to try to override it. If the method has to be public, you just put a note in the documentation for the method that it shouldn't be overridden or that subclasses should call super's implementation whichever the case may be. You'll find these kind of instructions all over in the documentation for Apple's own classes.
If you want your subclass to use the baseclass version of doInit then in the subclass don't declare a doInit method. Here's what I mean:
ClassA:
#interface ClassA :
-(void) doInit;
-(id) init;
#implementation
-(void) doInit {
NSLog(#"ClassA doInit");
}
-(id) init {
self = [super init];
if (self != NULL)
[self doInit];
return self;
}
ClassB
#interface ClassB : ClassA
-(id) init;
#implementation
-(id) init {
self = [super init];
if (self != NULL)
[self doInit];
return self;
}
And really you don't need to override the init method as well unless there's some special code that you want that class to do.
Please consider the following code:
`#interface Parent : NSObject
- (void)whoAmI;
#end
#implementation Parent
- (void)whoAmI
{
NSLog(#"PARENT CALLED");
}
#end
#interface Child : Parent
- (void)test;
#end
#implementation Child
- (void)whoAmI
{
NSLog(#"CHILD CALLED");
}
- (void)test
{
NSLog(#"%#", [super class]);// CHILD!!!! why???
[super performSelector:#selector(whoAmI)];// "CHILD CALLED" why???
}
#end
`
When i call test method i expect to see parent class printed and parent whoAmI method executed. But surprisingly both times the derived class is called. Can anyone explain why it happens and how do i performSelector: on base class?
The super method is simply a way of forwarding a message to a superclass' implementation code. However, self remains the same. In fact, if you create an instance of Child, there is no instance of Parent at all. You can test this by NSLog-ing self as a %p to inspect the pointer address; when a super method is called, the self pointer is the same as it was for the instance of the subclass which called it:
Parent:
- (void)printAddr {
NSLog(#"%p", self);
}
Child:
- (void)printAddr {
NSLog(#"sub: %p, self");
[super printAddr];
}
You will see that the pointers are the same if you call [aChild printAddr];.
Now let's translate this into addressing your specific questions. First off, look at the performSelector: method. Its default implementation is in NSObject, and this implementation most likely uses self to call the selector. Because of this, even though the method implementation is that of NSObject, the method will still be called on the real object, your subclass. If it weren't for this behavior, performSelector: would always try to call the method as if it were implemented directly on NSObject unless you implemented your own performSelector: on a subclass; obviously, this is the wrong behavior.
In addition, the same thing holds true for the -class method. Its default implementation resides on NSObject, and obviously it would be boring id it always returned [NSObject class], so instead it effectively uses self to get the class of the real object.
You can also test what I've said here by making a method on the superclass which calls another method on self. Even if you use super to call the first method, the second method will still be called on your subclass since self still points to the subclass:
Parent:
- (void)method {
NSLog(#"Parent: method");
[self method1];
}
- (void)method1 {
NSLog(#"Parent method1");
}
Child:
- (void)method {
[super method];
}
- (void)method1 {
NSLog(#"Child: method1");
}
In this case, [aChild method] will output:
Parent: method
Child: method1
Superclass is an NSOperation class which implements NSXMLParserDelegate - all it does is sending URL request and parsing XML data returned from a server. This class is being inherited by a subclass which also implements NSXMLParserDelegate. The parent's parser delegate is supposed to catch general error from the XML response before passing it on to child's parser delegate to do more specific parsing.
Within the superclass:
#implementation Super
#pragma mark NSOperation method
- (void) main {
id parentDelegate = [self getParserDelegate]; //?
id childDelegate = [self getParserDelegate]; //??
}
// I would like this to return parser delegate in the super class
- (id) getParserDelegate {
return self;
}
#end
Within subclass:
#implementation Sub
// main is not overidden in subclass
// and this should return parser delegate in the sub class
- (id) getParserDelegate {
return self;
}
#end
I'm instantiating the operation using the child's class i.e. Sub
Sub *theSub = [[Sub alloc] init];
[self.queue addOperation:theSub]; // Super's main method will be called
Within Super's main method I would like to have access to both parent and child's delegate but I found that 'self' always resolves to Sub, regardless whether 'self' is called within Sub or Super. Is it possible to call Super's getParserDelegate from within Super's main or is it just a bad design?
self is a pointer directly to the object. So it resolves to the same thing no matter where you inspect it along the inheritance chain. There is no such thing as a self pointer that would resolve directly to the superclass — that's the difference between the 'is a' school of extending object functionality and 'has a'.
Any messages issued to self will always be sent first to the most junior child class, then work their way up per the usual inheritance rules. As a result there's absolutely nothing you can provide to NSXMLParser that would cause delegate methods to go straight in to the super class.
I'd suggest that what you're describing with a common actor that implements most of the logic and a separate actor that does twiddly specifics is itself the delegate pattern. So what you probably want is to turn what you currently have as a parent into its own sovereign class and attach what you currently have as the child to it as a delegate. Just reuse the NSXMLParserDelegate protocol for that delegation relationship if it makes sense.
Super class never know about what its child's, and what they do.
//in Super.h
- (id) getParserDelegate;
//in Super.m
- (id) getParserDelegate {
return self;
}
//in Child.h
- (id) getParserDelegate;
//in Child.m
- (id) getParserDelegate {
return [super getParserDelegate];
}
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.