阅读 65

iOS底层探索--动态方法决议

小谷底层探索合集

  • 在上节博客(慢速查找分析)中提到了动态方法决议。今天我们就了解一波动态方法决议!!

1. 定位关键方法

    1. 我们已经知道在查找时(lookUpImpOrForward)中,在没有找到imp的最后,给了一次补救的机会
	// No implementation found. Try method resolver once.
	if (slowpath(behavior & LOOKUP_RESOLVER)) {
        behavior ^= LOOKUP_RESOLVER;
        return resolveMethod_locked(inst, sel, cls, behavior);
    }
复制代码

直接看也不知道这是啥,所以大家要养成会看注释的习惯。。

  • 注释的意思:没有发现实现。试图调用方法在解决一次

    1. 我们点进去先大致浏览下,然后重点分析(毕竟一开始就读懂每一行,感觉像个憨憨。。(原先我就是。。😆))

    1. 我们观察到:2个核心方法:resolveInstanceMethodresolveClassMethod

根据字面意思也知道,一个是解决实例方法,一个是解决类方法。

2. 探索分析

动态方法决议包括:对象的动态方法决议类的动态方法决议

2.1. 对象方法的动态方法决议

resolveInstanceMethod

2.1.1. 验证猜想

    1. 我们分析方法的时候,首先要找到关键的代码,哪一行取决定作用,我画了一波图分析(从后向前定位)

    1. 既然找到了这个关键的东西,那我们搜索下,这在哪里调用了

    1. 这个返回NO,感觉就是什么都没有处理~ :那就断点调试呗,看他是不是会走~
    1. 那我就掉一个有声明,没有实现的方法!!(大家也都知道,要走到补救的时候,说明在之前没有找到)

        XGTest *test = [XGTest alloc];
        Class tClass = [test class];
        [test say5];
复制代码

    1. 真会走~~~~!!那我们看下堆栈

这和我们分析的流程一样啊

2.1.2. 方法运用

这个时候我们会想到一种思路:如果我们重写一下这个方法,然后给sel关联一个imp,理论上是不是就没有问题了?

有兄弟说:你也知道是理论上!!,那我肯定要实践一波啊~

如果一个类有分类的话,优先调用分类的方法(相同方法)。(这个就不在这篇博客说明了啊)

    1. 我们创建NSObject分类,搞上一波(😆)
    1. 先创建后,打条log,证明他曾经来过。

    1. 看输出

他来过~~~

    1. 我们的思路:sel是没有知道不到对应的imp,所以崩掉。。我们给他个~
#import <objc/message.h>

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

+ (BOOL)resolveInstanceMethod:(SEL)sel{
    NSLog(@"%s--%@",__func__,NSStringFromSelector(sel));
    if (sel == @selector(say5)){
        IMP imp = class_getMethodImplementation(self, @selector(instanceMethodFix));
        Method method = class_getInstanceMethod(self, @selector(instanceMethodFix));
        const char * type = method_getTypeEncoding(method);
        return class_addMethod(self, sel, imp, type);
    }
    return NO;
}
复制代码

我们获取实例方法instanceMethodFiximp,然后把say5sel关联上他,兄弟们。我运行下.

他没有崩溃!!他没有崩溃!!他没有崩溃!!

    1. 结论:我们的分析是正确的,查找不到~苹果给了他一次补救的机会

走到这个方法里面说明方法没有找到的至于怎么用,就要看你们了

2.2. 类方法的动态方法决议

研究完对象的动态方法决议,类的就显得简单些了~

    1. 我们找到这个方法的核心

    1. 先搞一波调用类方法的代码
[XGTest say6];//方法没有实现
复制代码
    1. 我们可以继续在分类中创建方法!然后看是不是一样的原理!
- (void)classMethodFix{
    NSLog(@"%s",__func__);
}

+ (BOOL)resolveClassMethod:(SEL)sel{
    NSLog(@"%s--%@",__func__,NSStringFromSelector(sel));
    if (sel == @selector(say6)) {
        IMP imp = class_getMethodImplementation(self, @selector(classMethodFix));
        Method method = class_getClassMethod(self, @selector(classMethodFix));
        const char *type = method_getTypeEncoding(method);
        return class_addMethod(self, sel, imp, type);
    }
    return NO;
}
复制代码

** 我们运动打印一波,卧槽!崩了!!! **

    1. 这个时候,要引进一波,isa的走位图了

    1. 其实细心的兄弟们,应该也发现了个问题:就是在处理resolveClassMethod的时候

兄弟们,我创建的是NSObject的分类~(哈哈😆,也不知道这调皮的性格跟谁学的~)。至于怎么改成正确的大家应该也知道了(有些东西不能说的太明,否则会打消兄弟们探索的积极性~)

    1. 那这样的话其实就可以在resolveInstanceMethod中全部搞定了~
- (void)instanceMethodFix{
    NSLog(@"%s",__func__);
}

+ (BOOL)resolveInstanceMethod:(SEL)sel{
    NSLog(@"%s--%@",__func__,NSStringFromSelector(sel));
    if (sel == @selector(say5)){
        IMP imp = class_getMethodImplementation(self, @selector(instanceMethodFix));
        Method method = class_getInstanceMethod(self, @selector(instanceMethodFix));
        const char * type = method_getTypeEncoding(method);
        return class_addMethod(self, sel, imp, type);
    }else if (sel == @selector(say6)){
        IMP imp = class_getMethodImplementation(self, @selector(instanceMethodFix));
        Method method = class_getInstanceMethod(self, @selector(instanceMethodFix));
        const char * type = method_getTypeEncoding(method);
        return class_addMethod(self, sel, imp, type);
    }
    return NO;
}
复制代码

哈哈,发现就OK了~

最好的记录方法就是兄弟们自己去探索一波~👌