先上个 Swift 中的 demo:Method Swizzling

Swift 中的实现

其实 Swift 中实现原理和 OC 基本一致,只是苹果爸爸不再允许在 Swift 中使用+load()+initialize()方法,这当然难不倒各种大神,那么我就做次农夫山泉。。。

Swizzling

先抽取 swizzling 的实现到NSObject的扩展当中:

extension NSObject {static func swizzlingForClass(_ forClass: AnyClass, originalSelector: Selector, swizzledSelector: Selector) {guard let originalMethod = class_getInstanceMethod(forClass, originalSelector),let swizzledMethod = class_getInstanceMethod(forClass, swizzledSelector) else {return}let isAddSuccess = class_addMethod(forClass, originalSelector, method_getImplementation(swizzledMethod), method_getTypeEncoding(swizzledMethod))if isAddSuccess {class_replaceMethod(forClass, swizzledSelector, method_getImplementation(originalMethod), method_getTypeEncoding(originalMethod))} else {method_exchangeImplementations(originalMethod, swizzledMethod)}}
}
复制代码

可以看到核心实现和 OC 是完全一致的,那么剩下的就是模拟 OC 版本实现中的+load()dispatch_once

dispatch_once

我们用viewDidLoad来做个dispatch_once的示范:

extension UIViewController {static func swizzleViewDidLoad() {_ = self.swizzleMethod}@objc func swizzled_viewDidLoad() {swizzled_viewDidLoad()print("嘻嘻")}private static let swizzleMethod: Void = {let originalSelector = #selector(viewDidLoad)let swizzledSelector = #selector(swizzled_viewDidLoad)swizzlingForClass(UIViewController.self, originalSelector: originalSelector, swizzledSelector: swizzledSelector)}()
}
复制代码

在 Swift 中static let这样声明的变量其实已经用到dispatch_once,而且static自带lazy属性,要在封装函数swizzleViewDidLoad被调用时候才调用。

+load()

OC 中+load()方法会在类被装载时调用,确保需要用到的方法都是被 Swizzling 过的。Swift 中可以在AppDelegateinit方法中手动调用 swizzle 方法模拟+load()实现。

class AppDelegate: UIResponder, UIApplicationDelegate {override init() {super.init()UIViewController.swizzleViewDidLoad()}
}
复制代码

为什么要先调用 class_addMethod?

class_addMethod这个方法是很容易被人忽视的,对于 Swizzling 一节中的代码,还有一种常见的写法:

extension NSObject {static func swizzlingForClass(_ forClass: AnyClass, originalSelector: Selector, swizzledSelector: Selector) {guard let originalMethod = class_getInstanceMethod(forClass, originalSelector),let swizzledMethod = class_getInstanceMethod(forClass, swizzledSelector) else {return}method_exchangeImplementations(originalMethod, swizzledMethod)}
}
复制代码

这种方式就只是简单的直接交换了originalMethodswizzledMethod。乍一看貌似没有问题(其实最开始我绞尽脑汁也没想清楚到底哪里不对。。。),但是为什么各路大神都是用的第一种方式呢?网上有种说法:

要先尝试添加原 selector 是为了做一层保护,因为如果这个类没有实现原始方法"originalSel" ,但其父类实现了,那 class_getInstanceMethod 会返回父类的方法。这样 method_exchangeImplementations 替换的是父类的那个方法,这当然不是你想要的。所以我们先尝试添加 originalSel ,如果已经存在,再用 method_exchangeImplementations 把原方法的实现跟新的方法实现给交换掉。

其实这种说法已经算是比较明确问题所在了,但是愚笨的我还是没有想通到底为什就“这当然不是你想要的”了呢。

又是一番绞尽脑汁。。。终于 Biuer 的一下想通了

在举栗子前引用一段对 Selectors、Methods 和 Implementations 理解:

理解 selector, method, implementation 这三个概念之间关系的最好方式是:在运行时,类(Class)维护了一个消息分发列表来解决消息的正确发送。每一个消息列表的入口是一个方法(Method),这个方法映射了一对键值对,其中键值是这个方法的名字 selector(SEL),值是指向这个方法实现的函数指针 implementation(IMP)。 Method swizzling 修改了类的消息分发列表使得已经存在的 selector 映射了另一个实现 implementation,同时重命名了原生方法的实现为一个新的 selector。

假设父类有个方法method,子类未重写method方法,子类的中想要拿来替换的方法为swizzledMethod

  1. 用第二种方式进行方法交换

    • 在子类的实例中调用method方法时,确实按预期正常运行的
    • 在父类的实例中调用method方法时,就开始崩溃了。因为方法交换后,method方法的IMP其实和子类swizzledMethodIMP进行了交换,此时等同于父类调用子类方法,当然会崩溃。
  2. 用第一种方式进行方法交换

    class_addMethod先判断了子类中是否有method方法

    • 如果有,则添加失败,直接进行交换
    • 如果没有,则添加成功,将swizzledMethodIMP赋值给method这个Selector,然后在将methodIMP(其实是父类中的实现)赋值给swizzledMethod这个Selector

转载于:https://juejin.im/post/5cb6df44e51d456e6f45c6f1

Method Swizzling 为什么要先调用 class_addMethod?相关推荐

  1. iOS 开发:『Runtime』详解(二)Method Swizzling

    本文用来介绍 iOS 开发中『Runtime』中的黑魔法Method Swizzling. 通过本文,您将了解到: Method Swizzling(动态方法交换)简介 Method Swizzlin ...

  2. runtime实践之Method Swizzling

    利用 Objective-C 的 Runtime 特性,我们可以给语言做扩展,帮助解决项目开发中的一些设计和技术问题.这一篇,我们来探索一些利用 Objective-C Runtime 的黑色技巧.这 ...

  3. Method Swizzling的各种姿势

    因为Objective-C的runtime机制, Method Swizzling这个黑魔法解决了我们实际开发中诸多常规手段所无法解决的问题, 比如代码的插桩,Hook,Patch等等. 我们首先看看 ...

  4. 【原】iOS动态性(三) Method Swizzling以及AOP编程:在运行时进行代码注入

    概述 今天我们主要讨论iOS runtime中的一种黑色技术,称为Method Swizzling.字面上理解Method Swizzling可能比较晦涩难懂,毕竟不是中文,不过你可以理解为" ...

  5. Objective-C Runtime 运行时之四:Method Swizzling

    理解Method Swizzling是学习runtime机制的一个很好的机会.在此不多做整理,仅翻译由Mattt Thompson发表于nshipster的Method Swizzling一文. Me ...

  6. Objective-C Runtime (三):Method Swizzling(方法替换)

    Objective-C Runtime (三):Method Swizzling(方法替换) Method Swizzling是一种改变改变一个'selector'的实际实现的技术.通过这一技术,我们 ...

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

    在没有一个类的实现源码的情况下,想改变其中一个方法的实现,除了继承它重写.和借助类别重名方法暴力抢先之外,还有更加灵活的方法吗?在Objective-C编程中,如何实现hook呢?标题有点大,计划分几 ...

  8. runtime(四) method swizzling 与AOP编程

    什么是 AOP : (site: baike.baidu.com),引用百度百科中的解释就是: 在软件业,AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,通过 ...

  9. 关于Swift4.0 Method Swizzling(iOS的hook机制)使用

    2019独角兽企业重金招聘Python工程师标准>>> 关于Method Swizzling 原理什么的有很多帖子讲述的已经很清楚这里不再赘述, 这里仅仅处理Method Swizz ...

最新文章

  1. Xcode终端快捷键
  2. Ubuntu下Django+uWSGI+nginx部署
  3. 图像处理:python实现canny算子
  4. vscode php输出,js程序如何在vscode控制台输出
  5. linux让脚本在指定时间运行程序,如何限制Linux命令程序运行的时间
  6. Android APP 引导页实现-第一次应用进入时加载
  7. LeetCode之Move Zeroes
  8. iOS加入百度地图的几个问题
  9. 玩转FusionCharts:Y轴数字形式(如去掉K)
  10. #自定义多级菜单_怎么搞定排版中的多级列表问题?
  11. oracle除了什么之外,Oracle翻译
  12. Win-MASM64汇编语言-NEG指令
  13. C++网易云课堂开发工程师-操作符重载
  14. OSPF沉默接口配置
  15. PAT_乙级_1002_筱筱
  16. 程序人生—我已经努力了七年
  17. 什么是多态,多态的实现
  18. 自动化(四)appium真机远程控制和远程群控
  19. 带你深层次理解什么是进制
  20. 2019年,微信多开软件排行榜

热门文章

  1. Angular新建组件以及组件之间的调用
  2. Navicat控制mysql用户权限
  3. Tomcat下项目调整Log4J的console输出级别,减少输出信息
  4. Caused by: org.xml.sax.SAXParseException: cvc-elt.1: Cannot find the declaration of element 'beans'.
  5. 公告牌为什么有些是纸质,有些是电子的
  6. 科普丨营销人,还不知道行为触发的话你就 OUT 了!
  7. 神策 2021 数据驱动大会嘉宾阵容首曝 + 精彩观点前瞻
  8. 2017 数据驱动大会豪华议程出炉,早鸟票一周内全部售罄
  9. Python scrapy爬取京东,百度百科出现乱码,解决方案
  10. 挪动脑袋吃汉堡?AR版《贪吃蛇》帮你治好颈椎病