runtime+消息转发

2020-07-23  本文已影响0人  如果大雨落下

首先通过isa去类对象,查找方法,找不到就一层层往父类上找,如果最后还找不到,就走消息转发

  1. resolveInstanceMethod,
    如果在类对象上找不到方法,源码解释
    // No implementation found. Try method resolver once.

    if (slowpath(behavior & LOOKUP_RESOLVER)) {
        behavior ^= LOOKUP_RESOLVER;
        return resolveMethod_locked(inst, sel, cls, behavior);
    }

在这个resolveInstanceMethod可以动态的去添加方法,BOOL (*msg)(Class, SEL, SEL) = (typeof(msg))objc_msgSend;
bool resolved = msg(cls, resolve_sel, sel); 等于是执行了resolveInstanceMethod方法,并且获取里面的返回值,然后再去获取方法,如果里面动态添加了方法,就阔以执行了,如果没有,就走转发??

/***********************************************************************
* 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 resolveInstanceMethod(id inst, SEL sel, Class cls)
{
    runtimeLock.assertUnlocked();
    ASSERT(cls->isRealized());
    SEL resolve_sel = @selector(resolveInstanceMethod:);

    if (!lookUpImpOrNil(cls, resolve_sel, cls->ISA())) {
        // Resolver not implemented.
        return;
    }

    BOOL (*msg)(Class, SEL, SEL) = (typeof(msg))objc_msgSend;
    bool resolved = msg(cls, resolve_sel, 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(inst, sel, cls);

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

通过runtime ,动态的将没有的方法的实现指向另外一个方法的实现

+ (BOOL)resolveInstanceMethod:(SEL)sel
{
    if (sel == @selector(test)) {
        
        Method method = class_getInstanceMethod(self, @selector(otherMethod));
        IMP imp = method_getImplementation(method);
        class_addMethod(self, sel, imp, method_copyReturnType(method));
    }
    return YES;
}

增加C语言方法
C语言里面方法名就是对应的函数指针,IMP的实现
"v16@0:8" v代表返回值是void,16 代表参数占16个字节,@代表第一个参数是id类型,0代表第一个参数从0个字节开始,:代表第二个参数,8代表第二个参数从第8个字节开始
类方法需要添加到元类对象,获取元类对象:object_getClass(self)

void cotherTest(id self,SEL sel)
{
    printf("test00000");
    NSLog(@"%@ ----%@",self,NSStringFromSelector(sel));
}

+ (BOOL)resolveInstanceMethod:(SEL)sel
{
    if (sel == @selector(test)) {
        class_addMethod(self, sel, (IMP)cotherTest, "v16@0:8");
    }
    return YES;
}

+ (BOOL)resolveClassMethod:(SEL)sel
{
    if (sel == @selector(test)) {
        class_addMethod(object_getClass(self), sel, (IMP)cotherTest, "v16@0:8");
    }
    return YES;
}

以上均为方法解析阶段,如果在方法解析阶段,并没有顺利的进行方法的调用

进入消息转发阶段

- (id)forwardingTargetForSelector:(SEL)aSelector
{
    NSLog(@"herhe--%@",NSStringFromSelector(aSelector));
    return [[Cat alloc] init];;
}

这里可以返回一个新的对象来接收并执行这个方法,获取到对象后,在通过objc_sengdMsg(新的对象,aselector),即可以进行新对应方法的调用

如果并没有进行消息的转发,那会进入方法签名

- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector
{
    NSLog(@"herhe111--%@",NSStringFromSelector(aSelector));
    return return [NSMethodSignature signatureWithObjCTypes:"v16@0:8"]; //v16@0:8 返回值和参数
}

- (void)forwardInvocation:(NSInvocation *)anInvocation
{
    
}

这里可以将aSelector ,生成一个方法签名,并返回,然后在通过forwardInvocation 去实现调用
在forwardInvocation 里面可以执行任何想执行的东西,不写任何东西,也不会走到 unrecognized selector sent to instance

类方法同样也可以走这一套流程,只不过相应的方法需要换成 + (),即类方法,直接输入没有联想,修改即可,因为源码里面是直接通过对象指针调用的这个方法,如果用的类对象,则直接调用的是元类的类方法,所以直接将实例方法改为类方法,即可实现

上一篇 下一篇

猜你喜欢

热点阅读