1

I have a class A. B inherits A. Both classes implement method1 and method2.

method1 in A calls method2. It look like...

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

- (void)method2{
    // some work
}

method1 in B calls super class method 1, and B also overrides method2.

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

- (void)method2{
    // some work
}

Now, when B's instance is created and called method1 A's method1 calls method2 in B. What I want to do is calling A's method2 from A's method1 even when it is called from child(B).

In other words, in A's method1, I want to "forcefully" call the method in the same owner(class). Is there any easy way to do it? I think I can do it with calling objective-c runtime functions but I want to know if there is easier way.

I know that this is not the design we should make in usual case, but from a little complex reason I have to do it. So please don't propose me to change the design or ask me what is the original goal of the program.

Shu Suzuki
  • 1,164
  • 11
  • 19
  • I don't think you can. The dispatch table will take care of resolving the correct method at runtime and since the type is B and B has a overridden version of method2 that method will be invoked. Maybe through method swisseling this could be accomplished. – Peter Segerblom Nov 03 '15 at 08:43
  • You can do it by temporarily switching your instance to be of class A. But don't do that. – Avi Nov 03 '15 at 09:13
  • So, how can I switch the instance? – Shu Suzuki Nov 03 '15 at 09:14
  • I don't know what "switching your instance" would be but if he is talking about up-casting it won't work since up-casting do not change the type. – Peter Segerblom Nov 03 '15 at 16:13

1 Answers1

0

As a simplest solution I can come up with, use BOOL flag to decide how method2 should behave:

@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

Note that this solution is not thread safe.

UPD Another interesting way, using runtime magic:

@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

Other interesting ways to identify caller may be found in answers to this question

Community
  • 1
  • 1
Borys Verebskyi
  • 4,160
  • 6
  • 28
  • 42