从孩子调用时调用原始类方法

时间:2015-11-03 08:33:39

标签: objective-c

我有一个A类.B继承A.两个类都实现了method1和method2。

A中的method1调用method2。它看起来像......

- (void)method1{
    // some code
    [self method2];
    // some code
}

- (void)method2{
    // some work
}

B中的method1调用超类方法1,B也覆盖方法2。

- (void)method1{
    [super method1];
}

- (void)method2{
    // some work
}

现在,当B的实例被创建并调用method1时,方法1调用B中的method2。我想要做的是从A的方法1调用A的方法2,即使是从子(B)调用它。

换句话说,在A的方法1中,我想“强制”在同一所有者(类)中调用该方法。 有没有简单的方法呢?我想我可以通过调用objective-c运行时函数来实现它,但我想知道是否有更简单的方法。

我知道这不是我们在通常情况下应该做的设计,但是从一个复杂的原因我必须这样做。所以请不要建议我改变设计或问我该计划的最初目标是什么。

1 个答案:

答案 0 :(得分:0)

作为我能提出的最简单的解决方案,请使用BOOL标志来决定method2的行为方式:

@interface B ()
@property (nonatomic) BOOL shouldCallSuperMethod2;
@end

@implementation B

- (void)method1{
    self.shouldCallSuperMethod2 = YES;
    [super method1];
    self.shouldCallSuperMethod2 = NO;
}

- (void)method2{
    if (self.shouldCallSuperMethod2) {
        [super method2];
    }
    else {
        // some work
    }
}

@end

请注意,此解决方案不是线程安全的。

UPD 另一种有趣的方式,使用运行时魔术:

@import ObjectiveC.runtime;

@implementation B

- (void)method2 {
    NSUInteger returnAddress = (NSUInteger)__builtin_return_address(0);
    NSUInteger callerIMPAddress = 0;
    SEL interestingSelector = @selector(method1);

    // Iterate over the class and all superclasses
    Class currentClass = object_getClass(self);
    while (currentClass)
    {
        // Iterate over all instance methods for this class
        unsigned int methodCount;
        Method *methodList = class_copyMethodList(currentClass, &methodCount);
        unsigned int i;
        for (i = 0; i < methodCount; i++)
        {
            // Ignore methods with different selectors
            if (method_getName(methodList[i]) != interestingSelector)
            {
                continue;
            }

            // If this address is closer, use it instead
            NSUInteger address = (NSUInteger)method_getImplementation(methodList[i]);
            if (address < returnAddress && address > callerIMPAddress)
            {
                callerIMPAddress = address;
            }
        }

        free(methodList);
        currentClass = class_getSuperclass(currentClass);
    }

    if (callerIMPAddress == (NSUInteger)[self methodForSelector:interestingSelector]) {
        // method2 is called from method1, call super instead
        [super method2];
    }
    else {
        // some work
    }
}

@end

可以找到识别来电者的其他有趣方法in answers to this question