【问题标题】:Call original class method when called from child从子调用时调用原始类方法
【发布时间】:2015-11-03 08:33:39
【问题描述】:

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

A 中的method1 调用method2。好像……

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

- (void)method2{
    // some work
}

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

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

- (void)method2{
    // some work
}

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

换句话说,在A的method1中,我想“强制”调用同一个所有者(类)中的方法。 有什么简单的方法吗?我想我可以通过调用objective-c运行时函数来做到这一点,但我想知道是否有更简单的方法。

我知道这不是我们通常应该做的设计,但出于一个有点复杂的原因,我必须这样做。所以请不要建议我改变设计或问我程序的最初目标是什么。

【问题讨论】:

  • 我不认为你可以。调度表将负责在运行时解析正确的方法,并且由于类型是 B 并且 B 具有 method2 的覆盖版本,因此将调用该方法。也许通过方法 swisseling 这可以实现。
  • 您可以通过暂时将您的实例切换为 A 类来做到这一点。但不要这样做。
  • 那么,如何切换实例呢?
  • 我不知道“切换你的实例”会是什么,但如果他在谈论向上转换它就行不通,因为向上转换不会改变类型。

标签: objective-c


【解决方案1】:

作为我能想到的最简单的解决方案,使用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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-15
    • 1970-01-01
    • 1970-01-01
    • 2019-02-08
    • 1970-01-01
    相关资源
    最近更新 更多