Использование class_getInstanceMethod - где метод реализован в иерархии классов?

Можно ли найти где в иерархии классов метод, найденный class_getInstanceMethod идет от? Например, скажем, класс А реализует myMethod. Теперь скажите, что я подкласс класса А в классе А1. Если я позвоню class_getInstanceMethod(ClassA1, myMethod)Можно ли сказать, был ли полученный метод переопределен в Class A1 или поступает непосредственно из A1?

Я полагаю, что было бы возможно сравнить адреса памяти IMP, если бы у вас был доступ к ClassA и ClassA1, но у меня нет прямого доступа к A.

2 ответа

Решение

Вы всегда можете получить доступ к суперклассу класса, так что вы можете передать его class_getInstanceMethod или же class_getMethodImplementation с тем же SEL и сравнить IMP адреса, чтобы увидеть, был ли метод переопределен подклассом.

Это немного раздражает, если вы хотите получить корневой класс, который определяет этот метод.

Во всяком случае, здесь идет:

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;
}

Вот моя модифицированная версия кода Джейкоба. У меня были проблемы с ним, потому что class_getMethodImplementation возвращало _objc_msgForward, что вызывало обработку методов как переопределенных. Мне также не нужен *rootImpClass, но он достаточно прост, чтобы добавить обратно.

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;
}
Другие вопросы по тегам