18

In a subclass, I'm overriding a method that is not exposed in the super class. I know that I have the correct signature as it is successfully overriding the superclass implementation. However, as part of the the new implementation, I need to call the superclass's implementation from the subclass's implementation.

Because it's not exposed I have to invoke the method via a call to performSelector:

SEL superClassSelector = NSSelectorFromString(@"methodToInvoke");
[super performSelector:superClassSelector];

However, in my application this results in an infinite recursive loop where the subclass's implementation is invoked every time I try to invoke the superclass's implementation.

Any thoughts?

I realize this is an atypical situation but unfortunately there's no way to get around what I'm trying to do.

Chris Thompson
  • 35,167
  • 12
  • 80
  • 109

3 Answers3

45

The way I've dealt with this is to re-declare your super class' interface in your subclass implementation file with the method you want to call from the subclass

@interface MySuperclass()
- (void)superMethodIWantToCall;
@end

@implementation MySubclass


- (void)whateverFunction  {
    //now call super method here
    [super superMethodIWantToCall];
}

@end

I'm not sure if this is the best way to do things but it is simple and works for me!

ModernCarpentry
  • 3,127
  • 2
  • 20
  • 14
  • 1
    This is exactly the correct approach. In many cases you do this with a category rather than an extension (using `(Superclass)` rather than `()` in the interface), but it's the safest and cleanest approach in any case. – Rob Napier May 21 '13 at 20:30
  • Neat. Especially works nice to pass through delegate methods, just restate the protocol: `@interface UIScrollView () ` – Patrick Pijnappel Dec 05 '13 at 00:17
  • You can't pass validation when submitting app to AppStore by this way. Apple will reject :( – Tony Oct 04 '15 at 07:56
  • I've hit a similar problem myself where there is extremely good reason NOT to make a method signature public. I'm struggling to understand why - given that you choose to subclass a superclass - you don't simply inherit everything that goes with the superclass. If you want to override a method - then redefine it with, or without, a call to super. I can see why this approach might be safer with multiple inheritance - but not single inheritance. Can anyone give a definitive - Apple-approved - solution? – VectorVictor Oct 16 '17 at 16:21
9

This doesn't work because you're only sending performSelector:, not the selector you pass to that, to the superclass. performSelector: still looks up the method in the current class's method list. Thus, you end up with the same subclass implementation.

The simplest way to do this may be to just write in your own call to objc_msgSendSuper():

// Top level (this struct isn't exposed in the runtime header for some reason)
struct objc_super
{
    id __unsafe_unretained reciever;
    Class __unsafe_unretained superklass;
};

// In the subclass's method
struct objc_super sup = {self, [self superclass]};
objc_msgSendSuper(&sup, _cmd, other, args, go, here);

This can cause problems in the general case, as Rob Napier has pointed out below. I suggested this based on the assumption that the method has no return value.

jscs
  • 63,694
  • 13
  • 151
  • 195
  • 2
    In general you should not call `objc_msgSend*` unless there is a very strong reason to do so. The above code can crash or have ill-defined behavior depending on the exact return type of the method and processor you're running on. (You may have to call `objc_msgSendSuper_fpret()` for instance for some methods.) @ModernCarpentry's answer is the simplest and most correct way to do this. – Rob Napier May 21 '13 at 20:32
  • 1
    The direct call to the instance method is also a very complicated and fragile way to do this. It can fail if the method is defined in some way other than as a simple method definition (as a forwarding class for instance). There is no need for introspection here. – Rob Napier May 21 '13 at 20:39
  • @RobNapier: It didn't seem that OP was concerned about the return value, given the use of `performSelector:`, however I accept that your warning is sound in general. – jscs May 21 '13 at 20:42
  • It doesn't matter if you care about the return value. Different processors have different function calling ABIs for functions that return floating points or structs. So the function that returns the value may expect registers or stack space to be available that the caller does not expect needs to be protected. You'd have to look at every combination on each processor type to see which fail and how, but it's dangerous even if you ignore the return value. – Rob Napier May 21 '13 at 20:44
  • @RobNapier it is also dangerous to declare methods that you don't define, for the same reasons... I think this code shows that it should be something that you don't use as an every day tool and is a little "Hacky" in the good sense of the word – Grady Player May 21 '13 at 20:58
  • ... to detract from my previous point: in my example of this code, I did have to call the superclass's `-class` function so that the class object was set up correctly. – Grady Player May 21 '13 at 21:00
  • That's really interesting and for the purposes of the code I'm writing totally would have worked although the accepted answer is just plain easier. Thanks! – Chris Thompson May 21 '13 at 21:19
  • 1
    @RobNapier: You are of course quite correct. When I said "It seems he does not care" I should have been more accurate and said "It seems there _is_ no return value". – jscs May 22 '13 at 00:24
  • 1
    This is dangerous for the reasons discussed, but it's interesting to know how to do it anyway. – entonio Oct 13 '15 at 18:00
2

One way to go is to create a category of your class in a separate file with the method you are trying to expose

@interface MyClass (ProtectedMethods)

- (void)myMethod;

@end

and on the .m

@implementation MyClass (ProtectedMethods)

- (void)myMethod {

}

@end

Then, import this category from your .m files, and you're good to go. It's not the prettiest thing, but it'll do the trick

jscs
  • 63,694
  • 13
  • 151
  • 195
Ismael
  • 3,927
  • 3
  • 15
  • 23
  • This is largely the same as the accepted answer, with the important difference that you _must not_ define the method (no `@implementation` section), or you may very well clobber the existing implementation that you are trying to call. At best, it will be undefined which implementation is used. – jscs May 22 '13 at 04:22
  • It's not, because this way assures you that both the super and the sub class are referencing to the 'same method'. Say someone needs to change your superclass and changes the name of the method or adds a parameter to it, how will he know that you have a subclass somewhere that redefines the method? you'll get a crash. It really depends on what kind of project and how many devs you are working with. As a rule, I think that it's never good to redefine something that is already defined somewhere else – Ismael May 23 '13 at 19:42
  • As I read your answer, that's exactly what you're advocating: redefining the `myMethod` that's already defined in the superclass. – jscs May 23 '13 at 19:44
  • Not at all, i'm just defining it in an 'external header file', and both .m will know that it's originally defined there. The accepted answer post just re-defines it to get the compiler to work, while the subclass method definition may change. – Ismael May 23 '13 at 19:46
  • You're _declaring_ it in a header. The `@implementation` section re-_defines_ the method, and will clobber an existing _definition_ of `-[MyClass myMethod]`, meaning you won't be able to call the original. – jscs May 23 '13 at 19:47