26·iOS 面试题·什么是 Method Swizzling?

前言

Objective-C 是一门动态语言,在运行时期才能真正具体确定调用哪个函数。运行时,方法的大概调用逻辑如下:

在运行时,类(Class)维护了一个消息分发列表来解决消息的正确发送。每一个消息列表的入口是一个方法(Method),这个方法映射了一对键值对,其中键值是这个方法的名字 selector(SEL),值是指向这个方法实现的函数指针 implementation(IMP)。

Method swizzling 修改了类的消息分发列表使得已经存在的 selector 映射了另一个实现 implementation,同时重命名了原生方法的实现为一个新的 selector。

摘自:Method Swizzling

简单来说 Method Swizzling 就是替换方法列表中的 selector 对应的 IMP,达到方法交互的目的。

Method Swizzling 的实现方案

1、使用 class_addMethod、class_replaceMethod、method_exchangeImplementations

可以使用系统提供的方法来操作对象的方法列表,具体实现代码如下:

    SEL originalSeletor = originalSelector;
    SEL swizzledSeletor = swizzledSelector;
    
    Method originalMethod = class_getInstanceMethod(originalClass, originalSeletor);
    Method swizzledMethod = class_getInstanceMethod(swizzledClass, swizzledSeletor);
    if (!isInstanceMethod) {
        originalMethod = class_getClassMethod(originalClass, originalSeletor);
        swizzledMethod = class_getClassMethod(swizzledClass, swizzledSeletor);
    }
    
        //先尝试給源SEL添加IMP,这里是为了避免源SEL没有实现IMP的情况
    BOOL didAddMethod = class_addMethod(originalClass, originalSelector, method_getImplementation(swizzledMethod), method_getTypeEncoding(swizzledMethod));
    if (didAddMethod) {
        // class_addMethod 添加成功则说明:originalClass 没有实现 originalSelector 对应的方法,并且将此时已经将 originalSelector 对应实现改成 swizzledMethod 的实现。
        //添加成功:说明源SEL没有实现IMP,将源SEL的IMP替换到交换SEL的IMP
        class_replaceMethod(swizzledClass, swizzledSelector, method_getImplementation(originalMethod), method_getTypeEncoding(originalMethod));
    } else {
        // class_addMethod 添加失败则说明:originalClass 已经实现 originalSelector 对应的方法,不能再加了,这里直接交换实现就可以了。
        //添加失败:说明源SEL已经有IMP,直接将两个SEL的IMP交换即可
        method_exchangeImplementations(originalMethod, swizzledMethod);
    }

2、使用 RSSwizzle 库

对于上面的实现方式,在一般情况下是OK的,但是在场景特别复杂的时候会出现异常,例如子类、父类、分类多个类同时对一个方法进行交换,这里可能会出现问题,具体比较可以参阅:Objective-C Method Swizzling、iOS 界的毒瘤:Method Swizzle 中的例子。

RSSwizzle的主要思路是 hook 本类没有实现的方法(继承了父类的方法),不会去copy父类的实现并添加方法到本类中,而是在消息触发时动态去父类中查找以调用原来的实现。

3、如何选择

个人觉得对于小范围功能,并且可以确保不会多次替换方法,可以使用方案一直接进行方法替换;但是对于复杂的场景,推荐使用 RSSwizzle 库来进行方法替换。

Method Swizzling 注意事项

Method Swizzling 是一把双刃剑,使用不当也会给项目带来隐患,这里提一些注意事项:

1、尽量在 +load 方法中,并且用 dispatch_once 来确保交换顺序和线程安全

2、对于 Hook 的方法,需要调用原有实现,避免覆盖原有的方法,导致原有的功能缺失

3、方法命名推荐使用前缀,避免与原方法名字冲突

总结

Method Swizzling 相对于是比较底层的技术,平时开发中使用的不多,但是遇到特定的场景 Method Swizzling 却可以很优雅的解决问题;我们应该掌握 Method Swizzling 的实现原理,避免在项目中滥用,不然会导致很难排查的 Bug。

参考文献

iOS 界的毒瘤:Method Swizzle

Objective-C的hook方案(一): Method Swizzling

Objective-C Method Swizzling

RSSwizzle

Method Swizzling

你可能感兴趣的:(26·iOS 面试题·什么是 Method Swizzling?)