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

前言

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 SwizzlingiOS 界的毒瘤: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

学为好人。

讨论数量: 0
(= ̄ω ̄=)··· 暂无内容!

  • 请注意单词拼写,以及中英文排版,参考此页
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`, 更多语法请见这里 Markdown 语法
  • 支持表情,使用方法请见 Emoji 自动补全来咯,可用的 Emoji 请见 :metal: :point_right: Emoji 列表 :star: :sparkles:
  • 上传图片, 支持拖拽和剪切板黏贴上传, 格式限制 - jpg, png, gif
  • 发布框支持本地存储功能,会在内容变更时保存,「提交」按钮点击时清空
  请勿发布不友善或者负能量的内容。与人为善,比聪明更重要!