9.ios-runtime 初探 _objc_msgSend 方法动态决议

当我们查找方法时缓存中没有,父类也没有时,则最后会给 imp = forwarding。const IMP forward_imp = (IMP)_objc_msgForward_impcache; 这个是什么则是这次探索的目标

IMP lookUpImpOrForward(id inst, SEL sel, Class cls, int behavior)
{
...
const IMP forward_imp = (IMP)_objc_msgForward_impcache;

...
if (slowpath((curClass = curClass->getSuperclass()) == nil)) {
    // No implementation found, and method resolver didn't help.
    // Use forwarding.
    imp = forward_imp;
    break;
}
}
复制代码
  • _objc_msgForward_impcache 在源码中的位置,发现调用了 __objc_msgForward

image.png

image.png

  • __objc_forward_handler搜索不到则发现这个方法不是汇编,而搜索_objc_forward_handler 发现这个方法调用了 objc_defaultForwardHandler
  • 返回值 objc_defaultForwardHandler 上边的错误报错打印

image.png

错误方法的处理-消息处理

对象方法动态决议

  • 查看宏定义枚举值

584361F80A4DC9E23A201A2B2D785661.jpg

  • 进入这个方法判断中

2ABA92CEBFF2C361265872DD8253E81C.jpg

  • 查看behavior 等于3

image.png

  • 结果为2大于0 则进入方法

74FAF730064D970151AD37885E93B025.jpg

  • 进入后改变了behavior的值为1,再次判断的话则为0 不在进入,所以得出结论这个判断只会走一次。是一个单例。

123BB1AEF9EC8846593A0D73119079AE.jpg

static NEVER_INLINE IMP
resolveMethod_locked(id inst, SEL sel, Class cls, int behavior)
{
    runtimeLock.assertLocked();
    ASSERT(cls->isRealized());

    runtimeLock.unlock();
    // 程序员 你是不是傻 没有这个方法 - imp-nil
    // 奔溃 - 友善
    // 给你一次机会 拯救地球 -> imp
    if (! cls->isMetaClass()) {
        // try [cls resolveInstanceMethod:sel]
        resolveInstanceMethod(inst, sel, cls);  //对象方法进入
    } 
    else {
        // try [nonMetaClass resolveClassMethod:sel]
        // and [cls resolveInstanceMethod:sel]
        resolveClassMethod(inst, sel, cls); //类方法进入
        if (!lookUpImpOrNilTryCache(inst, sel, cls)) {
            resolveInstanceMethod(inst, sel, cls);  //由于类在元类中其实也是对象,最终调用父类会进入 NSObject 中,所以要再次调用对象方法的这个处理实现方法中。
        }
    }
    
    // chances are that calling the resolver have populated the cache
    // so attempt using it
    return lookUpImpOrForwardTryCache(inst, sel, cls, behavior);
}



复制代码

resolveInstanceMethod(inst, sel, cls);先进入这个方法处理完成后,调用 lookUpImpOrForwardTryCache 这个方法其实就是调用了_lookUpImpTryCache
这个其实就是消息发送的机制流程,先查找缓存方法,再调用lookUpImpOrForward方法。

对象方法进入 resolveInstanceMethod

static void resolveInstanceMethod(id inst, SEL sel, Class cls)
{
    runtimeLock.assertUnlocked();
    ASSERT(cls->isRealized());
    SEL resolve_sel = @selector(resolveInstanceMethod:);

    if (!lookUpImpOrNilTryCache(cls, resolve_sel, cls->ISA(/*authenticated*/true))) {
        // 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 = lookUpImpOrNilTryCache(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));
        }
    }
}

复制代码
  • 1.SEL resolve_sel = @selector(resolveInstanceMethod:); 获取这个方法保存到变量中
  • 2. BOOL (*msg)(Class, SEL, SEL) = (typeof(msg))objc_msgSend;

bool resolved = msg(cls, resolve_sel, sel);判断这个类是否能成功调用 resolveInstanceMethod方法

resolveInstanceMethod 处理

+ (BOOL)resolveInstanceMethod:(SEL)sel{
    // resolveInstanceMethod :LGTeacher-say666 为什么是两次 家庭作业
    // 处理 sel -> imp
    if (sel == @selector(say666)) {
        IMP sayNBImp     = class_getMethodImplementation(self, @selector(sayNB));
        Method method    = class_getInstanceMethod(self, @selector(sayNB));
        const char *type = method_getTypeEncoding(method);
        return class_addMethod(self, sel, sayNBImp, type);
    }
    
    NSLog(@"resolveInstanceMethod :%@-%@",self,NSStringFromSelector(sel));

    return [super resolveInstanceMethod:sel];
}
复制代码

resolveClassMethod

static void resolveClassMethod(id inst, SEL sel, Class cls)
{
    runtimeLock.assertUnlocked();
    ASSERT(cls->isRealized());
    ASSERT(cls->isMetaClass());

    if (!lookUpImpOrNilTryCache(inst, @selector(resolveClassMethod:), cls)) {
        // Resolver not implemented.
        return;
    }

    Class nonmeta;
    {
        mutex_locker_t lock(runtimeLock);
        nonmeta = getMaybeUnrealizedNonMetaClass(cls, inst);
        // +initialize path should have realized nonmeta already
        if (!nonmeta->isRealized()) {
            _objc_fatal("nonmeta class %s (%p) unexpectedly not realized",
                        nonmeta->nameForLogging(), nonmeta);
        }
    }
    BOOL (*msg)(Class, SEL, SEL) = (typeof(msg))objc_msgSend;
    bool resolved = msg(nonmeta, @selector(resolveClassMethod:), sel);

    // Cache the result (good or bad) so the resolver doesn't fire next time.
    // +resolveClassMethod adds to self->ISA() a.k.a. cls
    IMP imp = lookUpImpOrNilTryCache(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 resolveClassMethod:%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));
        }
    }
}
复制代码

resolveClassMethod 元类的以对象方法的方法

// 元类的以对象方法的方法
+ (BOOL)resolveClassMethod:(SEL)sel{
   
    NSLog(@"resolveClassMethod :%@-%@",self,NSStringFromSelector(sel));
    
    if (sel == @selector(sayHappy)) {
        IMP sayNBImp     = class_getMethodImplementation(objc_getMetaClass("LGTeacher"), @selector(sayKC));
        Method method    = class_getInstanceMethod(objc_getMetaClass("LGTeacher"), @selector(sayKC));
        const char *type = method_getTypeEncoding(method);
        return class_addMethod(objc_getMetaClass("LGTeacher"), sel, sayNBImp, type);
    }

    return [super resolveClassMethod:sel];
}

复制代码

image.png

创建分类来处理

#import <Foundation/Foundation.h>

NS_ASSUME_NONNULL_BEGIN

@interface NSObject (LG)

@end

NS_ASSUME_NONNULL_END


#import "NSObject+LG.h"
#import <objc/message.h>


@implementation NSObject (LG)

- (void)sayNB{
    NSLog(@"%@ - %s",self , __func__);
}

+ (void)sayKC{
    NSLog(@"%@ - %s",self , __func__);
}

#pragma clang diagnostic push
// 让编译器忽略错误
#pragma clang diagnostic ignored "-Wundeclared-selector"


+ (BOOL)resolveInstanceMethod:(SEL)sel{
    // resolveInstanceMethod :LGTeacher-say666 为什么是两次 家庭作业
    // 处理 sel -> imp
    
    NSLog(@"resolveInstanceMethod :%@-%@",self,NSStringFromSelector(sel));

    if (sel == @selector(say666)) {
        IMP sayNBImp     = class_getMethodImplementation(self, @selector(sayNB));
        Method method    = class_getInstanceMethod(self, @selector(sayNB));
        const char *type = method_getTypeEncoding(method);
        return class_addMethod(self, sel, sayNBImp, type);
    }else  if (sel == @selector(sayHappy)) {
        IMP sayNBImp     = class_getMethodImplementation(objc_getMetaClass("LGTeacher"), @selector(sayKC));
        Method method    = class_getInstanceMethod(objc_getMetaClass("LGTeacher"), @selector(sayKC));
        const char *type = method_getTypeEncoding(method);
        return class_addMethod(objc_getMetaClass("LGTeacher"), sel, sayNBImp, type);
    }
    return NO;
}
@end

复制代码

//#pragma clang diagnostic pop

/**

say666 () sel -> imp

1: 苹果给的一次机会
2: 全局 所有方法找不到 我们是不是都能监听
3: lg_model_traffic(项目) -> lg_home_didClickDetail (模块) -> pop home(处理) -> 发送消息didClickDetail (发送后台) -> 监控 改BUG
4: runtime ->
5: aop – oop 漫谈iOS AOP编程之路
6: 对象分工是非常明确 – 冗余代码 -> 提取 -> 公共的类 (强依赖 – 强耦合)
7: 无侵入 – 动态 注入代码 – 切入的方法 切入的类
8: 性能消耗 + 苹果写转发流程就没有意义
9: 消息转发流程 : 快速 + 慢速转发

*/

  • 1.由于可能写了一个不存在的方法报错奔溃,对用户的体验不好。系统给了一次机会来处理
  • 2.如果方法无法找到就必然会走一次 resolveInstanceMethod
  • 3
© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享