Subclass Override Prevention

Suppose there is an object that is so initialized

- (void)doInit { NSLog(@"In BaseClass init"); } - (id)init { self = [super init]; [self doInit]; return self; } 

and it has a subclass that is similarly introduced

 - (void)doInit { NSLog (@"In SubClass init"); } - (id)init { self = [super init]; [self doInit]; return self; } 

Now, if I create an instance of a child class, I get the following output:

 In SubClass init In SubClass init 

when really what i meant is

 In BaseClass init In SubClass init 

Is there a way to mark doInit to say that it cannot be redefined or do I need to create a unique name for all methods in the subclass?

I'm not quite sure how I have not come across this problem before, but there you go.

Edit:

I understand why this is happening, I did not expect the base class to be able to call an overridden function.

I also can't just call [super doInit]; from the Subclass method, because BaseClass still needs to call doInit to create an instance of BaseClass will still work. If I called [super doInit], I would still get a SubClass doInit call twice.

It seems like the answer is no, and I just need to uniquely name each doInit as doBaseClassInit and doSubClassInit.

+2
objective-c
source share
5 answers

If you have a method that does not need to be dynamically bound (i.e. you do not need to call the subclass method, if one exists), you need to do this as a function of C. Thus, you can 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 subclass:

 static void DoInit(SubClass *self) { NSLog(@"In SubClass init"); } - (id)init { self = [super init]; if (self) { DoInit(self); } return self; } 

Please note that both DoInit methods DoInit marked as static , therefore they are visible only for each compilation unit (.m file) and do not conflict with each other.

+3
source share

Perhaps you can try something similar in your base class. This means that when you execute init inside BaseClass , the doInit implementation for BaseClass .

 - (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 this is the narrowness of your need, this should work, as it bypasses the dynamic search method associated with sending a message. Hope this helps!

EDIT:

Disclaimer - If you are in this situation, this is probably not a good sign of the durability of your design. This method will make you work and work, but please document it carefully and consider ways to reorganize your code so that it is no longer used. Think that corrections like these will only be used if absolutely necessary.

+2
source share

The reason you are not getting the "In BaseClass init" console message is because your subclass does not call the doInit super implementation.

If you do not want doInit override the "best" way to avoid this, do not publish the existence of this method. Remove it from the header and name the method uniquely so that a collision is unlikely. For example, many private methods on Apple infrastructures have leading underscores. So, for example, you could name your method _doInit , and it is very unlikely that a subclass accidentally creates its own overriding implementation.

+1
source share

No, there is no forced way to prevent a subclass from overriding a method. The best thing you can do is not to put it in a common header file for the class so that someone does not try to override it. If the method should be publicly available, you simply put a note in the documentation for the method so that it is not overridden or that subclasses should call a super implementation, depending on what might be. You will find all of these instructions in the documentation for your own Apple classes.

+1
source share

If you want your subclass to use the base version of doInit, then do not declare the doInit method in the subclass. Here is 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; } 

Indeed, you do not need to override the init method unless there is special code that you want this class to execute.

0
source share

All Articles