1

So what I have is a class hierarchy as follows:

@interface MyClass : ParentClass

-(void)myMethod
{
    [super myMethod];
    // some specific operation
}

@end

@interface ParentClass : ParentParentClass

-(void)myMethod
{
    [super myMethod];
    // some specific operation
}

@end

@interface ParentParentClass : ParentParentParentClass

-(void)myMethod
{
    [super myMethod];
    // some specific operation
}

@end

Now let's say, in MyClass, I would like to avoid the call to the ParentClass's myMethod and instead want to call the myMethod in ParentParentClass.

How do I go about that?

I've already tried casting the super but that doesn't work.

rmaddy
  • 314,917
  • 42
  • 532
  • 579
P. Sami
  • 2,115
  • 2
  • 21
  • 39
  • `[super [super myMethod]];` should work. What errors are you getting? – Droppy Oct 02 '14 at 14:09
  • @Droppy I think you mean `[[super super] myMethod]` – Greg Oct 02 '14 at 14:19
  • @PartiallyFinite I do. – Droppy Oct 02 '14 at 14:21
  • 1
    Why would you want such a thing? – Popeye Oct 02 '14 at 14:24
  • @Popeye it sounds like some pretty bad design, which is probably why it's not possible. – Greg Oct 02 '14 at 14:27
  • @PartiallyFinite Yeah that's what I was thinking but you never know they may have some crazy reason that none of us can think of for doing it. – Popeye Oct 02 '14 at 14:30
  • @Popeye It's a third party library that I'm using and it breaks my code in only one method, I need the rest of it basically :) – P. Sami Oct 02 '14 at 14:31
  • Using the objc runtime, there should be a way to obtain the IMP of the method implementation of `ParentParentClass`, and then execute that directly. – Greg Oct 02 '14 at 14:32
  • well, this is just to illustrate my problem, the actual method is `viewWillAppear`... – P. Sami Oct 02 '14 at 14:33
  • You could I suppose try `(ParentParentClass *)[super myMethod];` though that is just a wild stab in the dark and not sure it will work. – Popeye Oct 02 '14 at 14:33
  • @PartiallyFinite I looked into `IMP` and got confused, could you please elaborate on that? – P. Sami Oct 02 '14 at 14:34
  • Or have a look at http://stackoverflow.com/questions/5102847/how-to-call-a-method-of-super-super – Popeye Oct 02 '14 at 14:34
  • @Popeye, casting doesn't work, in general, you're not allowed to cast `super` – P. Sami Oct 02 '14 at 14:35
  • In general would be a bad design but hey we're throwing that out the window, but yeah I didn't think it would work. Have a look at that link though it shows how to implement what `PartiallyFinite` is talking about. – Popeye Oct 02 '14 at 14:36
  • @Popeye I know it's a strange and annoying hack, but let's face it, it's very case specific. – P. Sami Oct 02 '14 at 14:40
  • It's not my design and you don't need to justify it to use, i was just saying it seems bad. – Popeye Oct 02 '14 at 14:42
  • @P.Sami Fix the library or replace it. This will assuredly lead to pain in the future. Do you know absolutely for sure that super's implementation doesn't have any side effects that the library otherwise relies upon? – bbum Oct 02 '14 at 17:19

4 Answers4

6

You should be able to do this:

Method method = class_getInstanceMethod([ParentParentClass class], @selector(myMethod));
IMP imp = method_getImplementation(method);
((void (*)(id, SEL))imp)(self, @selector(myMethod)); // cast the function to the correct signature

You may need to #import <objc/runtime.h> in order for this to compile.

This gets the actual C function that the method is translated to at compile time, which you can then call. When the Objective-C compiler compiles your code, all methods are translated into plain C functions, which take self as their first argument, and _cmd, the selector for the current method as the second argument, followed by all of the other arguments that the Objective-C method takes. class_getInstanceMethod obtains the runtime representation of the given method (including various metadata), and method_getImplementation gets the plain C function pointer from that method.

If you look at the Objective-C runtime header, you'll see that the IMP type is defined as typedef id (*IMP)(void);, so you need to cast it to the actual type of the method implementation function, which will be (return_type (*)(id, SEL, method_arguments_in_order)) — the function takes self and the method selector as its first two arguments, followed by the ObjC method parameters.

So, once you have the standard C function pointer, you can simply call it as you would call a function.

I wouldn't go so far as to call this approach hacky, but it certainly is non-standard, as made clear by the need to use the underlying runtime methods directly to achieve what you want. I would definitely consider this a better solution, in terms of design, reliability and making sense, than adding bridging methods in the superclass that call its superclass' methods.

Valentin Shergin
  • 7,166
  • 2
  • 50
  • 53
Greg
  • 9,068
  • 6
  • 49
  • 91
  • 1
    @P.Sami **It is hacky**. Very hacky. Having to call a method in super's super is a sure indication of bad design. It is also fragile and rife with issues. Namely, it assumes that super's implementation can be bypassed and, thus, breaks encapsulation. – bbum Oct 02 '14 at 17:18
  • @bbum I understand/know it's against the encapsulation principle but sometimes you gotta do what you gotta do... It's good to know that there is a technique to get to the super.super though ;) – P. Sami Oct 02 '14 at 19:23
  • @bbum I was referring more to the fact that this is probably the *least hacky* possible solution to a hacky problem. – Greg Oct 02 '14 at 23:05
  • @P.Sami note that we aren't actually at any point retrieving `super.super` as such, we're simply directly retrieving the instance method implementation provided by a specific class that you specify by name. – Greg Oct 02 '14 at 23:07
1

super isn't an object, it's a special keyword that tells the compiler to emit calls to objc_msgSendSuper instead of objc_msgSend.

Since there is no such function as objc_msgSendSuperSuper, what you want can't be done.

You'll have to rely on a method with a different selector instead.

cobbal
  • 69,903
  • 20
  • 143
  • 156
0

This is really a very bad practice,
Anyway, if you need to do that, you need to make a bridge method for invoking

@interface MyClass : ParentClass

-(void)myMethod
{
    // INVOKE BRIDGE
    [super myMethodSuperBridge];
    // some specific operation
}

@end

@interface ParentClass : ParentParentClass

-(void)myMethod
{
    [super myMethod];
    // some specific operation
}

- (void)myMethodSuperBridge
{
    [super myMethod];
}
@end

@interface ParentParentClass : ParentParentParentClass

-(void)myMethod
{
    [super myMethod];
    // some specific operation
}
l0gg3r
  • 8,864
  • 3
  • 26
  • 46
0

you can do like this

(void(*)(struct objc_super *, SEL, id))objc_msgSendSuper)(&(struct objc_super){self, [[self superclass] superclass]}, @selector(myMethod));

maybe you want to call super.super.super method, you can do like this

(void(*)(struct objc_super *, SEL, id))objc_msgSendSuper)(&(struct objc_super){self, [[[self superclass] superclass] superclass]}, @selector(myMethod));
PoiSon
  • 1