Joiner
Joiner

Reputation: 71

Dynamic Method Resolution

i have known runtime gives the message a chance to be executed when If the implementation of the method is not found in the class or in its superclasses. It will start by sending the message + (BOOL)resolveInstanceMethod:(SEL)name to the class of the object: this method allows you to add the method at runtime to the class: if this message returns YES, it means it can redispatch the message.i try to return NO from + (BOOL)resolveInstanceMethod:(SEL)name, i thought that will make -(id)forwardingTargetForSelector:(SEL)aSelector be invoked(and dynamicMethodIMP will not be invoked), but still dynamicMethodIMP is invoked as same as the return value is YES. the Apple Doc said

If the method returns NO, the Objective-C runtime will pass control to the method forwarding mechanism(https://developer.apple.com/library/mac/releasenotes/Foundation/RN-FoundationOlderNotes/)

what is the different between return YES and return No from + (BOOL)resolveInstanceMethod:(SEL)name. a piece of sample code like this :

`void dynamicMethodIMP(id self, SEL _cmd) {
    NSLog(@"%@ has added", NSStringFromSelector(_cmd));
}
+(BOOL)resolveInstanceMethod:(SEL)sel {
    if (sel == @selector(mustHas)) {
        class_addMethod([self class], sel, (IMP) dynamicMethodIMP, "v@:");
        return NO;
    }
    return [super resolveInstanceMethod:sel];
}` 
[obj mustHas];

Upvotes: 2

Views: 481

Answers (1)

Rob Napier
Rob Napier

Reputation: 299623

The docs you're reading are from the 10.5 release notes, but that specific statement is not part of the current documentation.

The implementation of class_resolveInstanceMethod() actually ignores your return value. It just checks if you've implemented +resolveInstanceMethod, calls it if you have, and then it looks up the original selector (just to cache the result). The only time your return value matters is when debugging the runtime.

There's no need to guess at how message forwarding works. It's all open source. Here's the function that calls +resolveInstanceMethod (runtime/objc-class.mm):

/***********************************************************************
* _class_resolveInstanceMethod
* Call +resolveInstanceMethod, looking for a method to be added to class cls.
* cls may be a metaclass or a non-meta class.
* Does not check if the method already exists.
**********************************************************************/
static void _class_resolveInstanceMethod(Class cls, SEL sel, id inst)
{
    if (! lookUpImpOrNil(cls->ISA(), SEL_resolveInstanceMethod, cls, 
                         NO/*initialize*/, YES/*cache*/, NO/*resolver*/)) 
    {
        // Resolver not implemented.
        return;
    }

    BOOL (*msg)(Class, SEL, SEL) = (typeof(msg))objc_msgSend;
    BOOL resolved = msg(cls, SEL_resolveInstanceMethod, sel);

    // Cache the result (good or bad) so the resolver doesn't fire next time.
    // +resolveInstanceMethod adds to self a.k.a. cls
    IMP imp = lookUpImpOrNil(cls, sel, inst, 
                             NO/*initialize*/, YES/*cache*/, NO/*resolver*/);

    if (resolved  &&  PrintResolving) {
        if (imp) {
            _objc_inform("RESOLVE: method %c[%s %s] "
                         "dynamically resolved to %p", 
                         cls->isMetaClass() ? '+' : '-', 
                         cls->nameForLogging(), sel_getName(sel), imp);
        }
        else {
            // Method resolver didn't add anything?
            _objc_inform("RESOLVE: +[%s resolveInstanceMethod:%s] returned YES"
                         ", but no new implementation of %c[%s %s] was found",
                         cls->nameForLogging(), sel_getName(sel), 
                         cls->isMetaClass() ? '+' : '-', 
                         cls->nameForLogging(), sel_getName(sel));
        }
    }
}

Upvotes: 1

Related Questions