8

Is it possible to find where in a class hierarchy the method retrieved by class_getInstanceMethod is coming from? For example, say Class A implements myMethod. Now say i've subclassed Class A in Class A1. If I call class_getInstanceMethod(ClassA1, myMethod), is it possible to tell whether the resulting method has been overridden in ClassA1 or comes directly from A1?

I suppose it would be possible to compare the memory addresses of the IMPs if you had access to both ClassA and ClassA1, but I don't have direct access to A.

Jacob Relkin
  • 161,348
  • 33
  • 346
  • 320
Sean Danzeiser
  • 9,141
  • 12
  • 52
  • 90

2 Answers2

15

You can always access a class' superclass, so you can pass it along to class_getInstanceMethod or class_getMethodImplementation with the same SEL and compare the IMP addresses to see if the method was overridden by the subclass.

This gets a bit hairier if you want to get at the root class which defines this method.

Anyway, here goes:

static inline BOOL isInstanceMethodOverridden(Class cls, SEL selector, Class *rootImpClass) {
    IMP selfMethod = class_getMethodImplementation(cls, selector);
    BOOL overridden = NO;
    Class superclass = [cls superclass];
    while(superclass && [superclass superclass]) {
        IMP superMethod = class_getMethodImplementation(superclass, selector);
        if(superMethod && superMethod != selfMethod) {
            overridden = YES;
            if(!rootImpClass) {
                //No need to continue walking hierarchy
                break;
            }
        }

        if(!superMethod && [cls respondsToSelector:selector])) {
            //We're at the root class for this method
            if(rootImpClass) *rootImpClass = cls;
            break;
        }

        cls = superclass;
        superclass = [cls superclass];
    }

    return overridden;
}
Jacob Relkin
  • 161,348
  • 33
  • 346
  • 320
3

Here's my modified version of Jacob's code. I had problems with his because class_getMethodImplementation was returning an _objc_msgForward which was causing methods to be treated as overridden. I also didn't need *rootImpClass but it's simple enough to add back in.

inline BOOL isInstanceMethodOverridden(Class cls, SEL selector)
{
    IMP selfImplementation = class_getMethodImplementation(cls, selector);

    BOOL overridden = NO;
    Class superclass = cls;

    while ((superclass = [superclass superclass]))
    {
        Method superMethod = class_getInstanceMethod(superclass, selector);
        if (superMethod && method_getImplementation(superMethod) != selfImplementation)
        {
            overridden = YES;
            break;
        }
    }

    return overridden;
}
David Lawson
  • 7,802
  • 4
  • 31
  • 37