8

Another way of phrasing this question: is it possible for a subclass to be a delegate of its super class? I'm trying to make my code reusable within my app and have a situation where the subsclass needs to implement two methods for it to be functional. How can I ensure this occurs? Or what is the proper way of defining these methods?

Update

I didn't mean to imply that I want the compiler to generate flags. I just want a clean way of organizing my code. Currently I override methods of the superclass. Using that approach the superclass can call [super methodToOverride] and it works. However this doesn't feel very clean to me as there's no way to specify "these are the methods you should override" aside from putting a comment somewhere.

Nick ONeill
  • 7,341
  • 10
  • 47
  • 61
  • Why do you need to enforce this in code? Are you saying there will be multiple possible subclasses, written by different people? Does the superclass implement these methods? – jscs Mar 17 '14 at 19:34
  • you could implement such method's body with a default throwing an exception content )with a message like _this method should be overridden_), that forces the method will be overridden in the subclass; not too elegant but does the job quite accurately. – holex Jan 22 '18 at 16:21

6 Answers6

8

In obj-c, it is not possible to force subclasses to overwrite methods of its superclass. But you can raise an exception in the superclass, should it ever be called because the subclass did not implement a certain method. But a subclass can be a delegate of its superclass, if the superclass does not implement certain methods, and you can enforce that the delegate implements these methods, if the superclass specifies the protocol, i.e. required methods, and the subclass adopts it.

Reinhard Männer
  • 14,022
  • 5
  • 54
  • 116
  • so you would make `self.delegate = self`? – Nick ONeill Mar 17 '14 at 20:10
  • I would make instanceOfSuperclass.delegate = instanceOfSubclass if it implements the required methods. – Reinhard Männer Mar 17 '14 at 20:16
  • 1
    the `self` I was referring to is the subclass of superclass. – Nick ONeill Mar 17 '14 at 21:17
  • @Nick: Sorry, I was yesterday night to tired to see your point. Yes, if "delegate" is a property of the superclass, the the statement self.delegate = self, executed in the subclass, assigns the "self" of the subclass to the delegate of the superclass. And if the superclass requires per protocol that the subclass, which adopts the protocol, implements a method "test", then the statement [self.delegate test] in the subclass executes "test" in the subclass. I hope I got you right this time. – Reinhard Männer Mar 18 '14 at 06:28
  • @ReinhardMänner I think this will create a reference cycle right? – Michael May 01 '15 at 19:18
  • @confile Generally one should use weak references for delegates to avoid reference cycles, see – Reinhard Männer May 02 '15 at 09:32
7

If you want to force your subclass to implement methods from super class, you can do this as below:

//In super class
- (id)someMethod:(SomeObject*)bla
{
     [self doesNotRecognizeSelector:_cmd];
     return nil;
}

Your app will crash if subclass will not implement this method and you don't need to call

[super someMethod:bla];
sanjana
  • 3,034
  • 2
  • 25
  • 31
  • In buildtime or in runtime? – Jorge Gil Jul 07 '19 at 01:22
  • The problem is that as a programmer you want to detect the problem as soon as possible. i.e. at compile time or at _linter_ time. If you do not implement something by mistake you will detect it at runtime (a later stage of the development process) which is typically more costly to solve in terms of time and money – tomacco Nov 22 '19 at 15:07
4

There is no way to do this in compile time. However you can raise an exception in the base class.

Something like this:

@throw [NSException exceptionWithName:NSInternalInconsistencyException
                               reason:[NSString stringWithFormat:@"You must override %@ in a subclass", NSStringFromSelector(_cmd)]
                             userInfo:nil];
johan
  • 6,578
  • 6
  • 46
  • 68
2

If your question is "how can I get the compiler to flag that a certain class doesn't implement a certain function" then I would say

  1. Define a protocol with non-optional methods -- "By default, all methods declared in a protocol are required methods. This means that any class that conforms to the protocol must implement those methods."
  2. Define a class ("stub") that declares it implements the protocol
  3. Now when a subclass of your stub class is written, the compiler will flag it as an error if the mandatory method(s) aren't implemented
software evolved
  • 4,314
  • 35
  • 45
  • I went at this a slightly different way. I defined a protocol with the mandatory method I wanted to be able to force the subclass to implement. I added a stub to the base class, so I could call the method there. Then I declared support for the protocol in the subclass. When the subclass didn't implement the method, the compiler issued a warning (not an error) on the subclass' @implementation line saying that it had not implemented the method defined in the protocol. – Carl Smith Dec 09 '16 at 20:57
  • Why did it issue a warning if the base class had the method? – malhal Dec 31 '17 at 09:04
  • @CarlSmith but wouldn't you have to remember to declare that the subclass adopt the protocol? – Pavan May 31 '18 at 13:11
  • No, you just have to remember to derive from the stub class at some point in the class hierarchy. – software evolved Jun 04 '18 at 16:58
1

I know that it's awful, but supposed that you need to do this since your 3rdParty SDK requires this design pattern, you could use a Factory pattern:

Supposed then to have the base class MyParentAPIClient and two sub classes like MyFacebookAPIClient and MyGooglePlusAPIClient and that you do something like

self.myAPIClient = [MyParentAPIClient alloc] initWithAPIKey:apiKey];

and that you have defined

@@interface MyParentAPIClient : NSObject {
}

-(void)callAPI;

@end

and you have override this in the two subclasses

@implementation MyFacebookAPIClient
 -(void)callAPI {
    [super callAPI];
    // do something specific for this api client
 }
@end

and

@implementation MyGooglePlusAPIClient
-(void)callAPI {
    [super callAPI];
    // do something specific for this api client
 }
@end

Then you are doing in your controller

[self.myAPIClient callAPI];

but the super class MyParentAPIClient method is being called.

Now you could do a factory in the base class like:

-(void)callAPI {
    if([self isKindOfClass:[MyFacebookAPIClient class]]) {
        [((MyFacebookAPIClient*)self) callAPI];
    } else if([self isKindOfClass:[MyGooglePlusAPIClient class]]) {
       [((MyGooglePlusAPIClient*)self) callAPI];
    }
 }

Of course this have a downside that is to do not call the super in the sub classes that now become:

@implementation MyFacebookAPIClient
 -(void)callAPI {
    // [super callAPI]; the factory method called that
    // do something specific for this api client
 }
@end

and

    @implementation MyGooglePlusAPIClient
    -(void)callAPI {
        // [super callAPI]; being called in the factory
        // do something specific for this api client
     }
    @end

The good news is that there is no change in the methods calls since as soon as you call from the controller:

 [self.myAPIClient callAPI];

You will have the calls

[MyParentAPIClient callAPI]; // parent class
[MyFacebookAPIClient callAPI]; // sub class

The other downside is that the parent class must known the subclass instances.

Now if we take a look at the factory:

if([self isKindOfClass:[MyFacebookAPIClient class]]) {
        [((MyFacebookAPIClient*)self) callAPI];
    } else if([self isKindOfClass:[MyGooglePlusAPIClient class]]) {
       [((MyGooglePlusAPIClient*)self) callAPI];
    }
  }

we could make it better like in several way. Take a look at Dynamic type cast from id to class in objective c and Is there an equivalent to C++'s dynamic cast in Objective-C? or Objective-C dynamic_cast?

Good luck!

Community
  • 1
  • 1
loretoparisi
  • 15,724
  • 11
  • 102
  • 146
1

The UIGestureRecognizerSubclass.h pattern from UIKit is worth a look, that has all the protected methods that should be overridden and that header is not in the framework include, it is only included in subclasss' .m files. Also, nowadays you can tag methods with NS_REQUIRES_SUPER to require overrides to call super, however it can only be used in interfaces, not protocols so that might influence your design.

For super advanced code, NSAccessibilityProtocols.h in AppKit uses a protocol tag to require subclasses to re-implement methods, even if already implemented by a superclass. Here is an example of that you can paste right into in header in your currently open Xcode project:

NS_PROTOCOL_REQUIRES_EXPLICIT_IMPLEMENTATION
@protocol Protocol
@property (readonly) id theWorstOfTimes;
// -(void)testMethod; // uncomment to test problem
@end

// In this example, ClassA adopts the protocol.
@interface ClassA : NSObject <Protocol>
@property (readonly) id theWorstOfTimes;
@end

@implementation ClassA
- (id)theWorstOfTimes{
    return nil; // default implementation does nothing
}
-(void)testMethod{}
@end

// This class subclasses ClassA (which also adopts 'Protocol').
@interface ClassB : ClassA <Protocol>
@end

@implementation ClassB // expected-warning {{property 'theWorstOfTimes' requires method 'theWorstOfTimes' to be defined - use @synthesize, @dynamic or provide a method implementation in this class implementation}} 
@end

In Xcode you'll see a yellow line at ClassB's expected-warning that the property method is missing. NS_PROTOCOL_REQUIRES_EXPLICIT_IMPLEMENTATION is just a macro for __attribute__((objc_protocol_requires_explicit_implementation)) and this code sample is modified from the test harness of that feature here.

Although this looks great there is a slight problem. Currently this only works for methods that implement protocols, it used to work also for methods but a bug has been introduced in 2014 via a misunderstanding on the purpose of this feature and thus now it is limited to property methods. I have emailed the author to make them aware so hopefully it changed back to its original and proper behavior. To test the bug you can uncomment the method in the protocol and you will see there is no warning in ClassB. Hopefully you can change some of your methods to read-only properties to at least get some use out of it. On the plus side when Xcode offers to "Fix" the issue it does add stubs for the missing methods.

Here is some documentation on NS_PROTOCOL_REQUIRES_EXPLICIT_IMPLEMENTATION: ImplementingAccessibilityforCustomControls nsaccessibilitybutton

If you used this then pat yourself on the back for becoming an ObjC expert if you weren't already!

malhal
  • 26,330
  • 7
  • 115
  • 133
  • Actually, the question was about methods, not about properties, and for properties it does **not** work, as you said (maybe due to a bug). But your answer is anyway interesting, thanks! – Reinhard Männer Jan 22 '18 at 10:33
  • I guess you misunderstood, it does work for properties. It will also work for methods again once they undo that bug fix I mentioned. – malhal Jan 22 '18 at 10:34
  • I think I understood you right: **It does not work for methods since 2014**, but **maybe** it will work again in the future. And the question was about **methods**. – Reinhard Männer Jan 22 '18 at 10:41
  • Hopefully we can get it fixed. I added some other thoughts I had to the beginning of the answer, maybe you can make use of NS_REQUIRES_SUPER. – malhal Jan 22 '18 at 15:39
  • Also when Xcode offers to "Fix" the issue, it does correctly add the method stubs. – malhal Jan 22 '18 at 16:35