Method Swizzling 为什么要先调用 class_addMethod?
先上个 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 中可以在AppDelegate
的init
方法中手动调用 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)}
}
复制代码
这种方式就只是简单的直接交换了originalMethod
和swizzledMethod
。乍一看貌似没有问题(其实最开始我绞尽脑汁也没想清楚到底哪里不对。。。),但是为什么各路大神都是用的第一种方式呢?网上有种说法:
要先尝试添加原 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
。
用第二种方式进行方法交换
- 在子类的实例中调用
method
方法时,确实按预期正常运行的 - 在父类的实例中调用
method
方法时,就开始崩溃了。因为方法交换后,method
方法的IMP
其实和子类swizzledMethod
的IMP
进行了交换,此时等同于父类调用子类方法,当然会崩溃。
- 在子类的实例中调用
用第一种方式进行方法交换
class_addMethod
先判断了子类中是否有method
方法- 如果有,则添加失败,直接进行交换
- 如果没有,则添加成功,将
swizzledMethod
的IMP
赋值给method
这个Selector
,然后在将method
的IMP
(其实是父类中的实现)赋值给swizzledMethod
这个Selector
转载于:https://juejin.im/post/5cb6df44e51d456e6f45c6f1
Method Swizzling 为什么要先调用 class_addMethod?相关推荐
- iOS 开发:『Runtime』详解(二)Method Swizzling
本文用来介绍 iOS 开发中『Runtime』中的黑魔法Method Swizzling. 通过本文,您将了解到: Method Swizzling(动态方法交换)简介 Method Swizzlin ...
- runtime实践之Method Swizzling
利用 Objective-C 的 Runtime 特性,我们可以给语言做扩展,帮助解决项目开发中的一些设计和技术问题.这一篇,我们来探索一些利用 Objective-C Runtime 的黑色技巧.这 ...
- Method Swizzling的各种姿势
因为Objective-C的runtime机制, Method Swizzling这个黑魔法解决了我们实际开发中诸多常规手段所无法解决的问题, 比如代码的插桩,Hook,Patch等等. 我们首先看看 ...
- 【原】iOS动态性(三) Method Swizzling以及AOP编程:在运行时进行代码注入
概述 今天我们主要讨论iOS runtime中的一种黑色技术,称为Method Swizzling.字面上理解Method Swizzling可能比较晦涩难懂,毕竟不是中文,不过你可以理解为" ...
- Objective-C Runtime 运行时之四:Method Swizzling
理解Method Swizzling是学习runtime机制的一个很好的机会.在此不多做整理,仅翻译由Mattt Thompson发表于nshipster的Method Swizzling一文. Me ...
- Objective-C Runtime (三):Method Swizzling(方法替换)
Objective-C Runtime (三):Method Swizzling(方法替换) Method Swizzling是一种改变改变一个'selector'的实际实现的技术.通过这一技术,我们 ...
- Objective-C的hook方案(一): Method Swizzling
在没有一个类的实现源码的情况下,想改变其中一个方法的实现,除了继承它重写.和借助类别重名方法暴力抢先之外,还有更加灵活的方法吗?在Objective-C编程中,如何实现hook呢?标题有点大,计划分几 ...
- runtime(四) method swizzling 与AOP编程
什么是 AOP : (site: baike.baidu.com),引用百度百科中的解释就是: 在软件业,AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,通过 ...
- 关于Swift4.0 Method Swizzling(iOS的hook机制)使用
2019独角兽企业重金招聘Python工程师标准>>> 关于Method Swizzling 原理什么的有很多帖子讲述的已经很清楚这里不再赘述, 这里仅仅处理Method Swizz ...
最新文章
- Xcode终端快捷键
- Ubuntu下Django+uWSGI+nginx部署
- 图像处理:python实现canny算子
- vscode php输出,js程序如何在vscode控制台输出
- linux让脚本在指定时间运行程序,如何限制Linux命令程序运行的时间
- Android APP 引导页实现-第一次应用进入时加载
- LeetCode之Move Zeroes
- iOS加入百度地图的几个问题
- 玩转FusionCharts:Y轴数字形式(如去掉K)
- #自定义多级菜单_怎么搞定排版中的多级列表问题?
- oracle除了什么之外,Oracle翻译
- Win-MASM64汇编语言-NEG指令
- C++网易云课堂开发工程师-操作符重载
- OSPF沉默接口配置
- PAT_乙级_1002_筱筱
- 程序人生—我已经努力了七年
- 什么是多态,多态的实现
- 自动化(四)appium真机远程控制和远程群控
- 带你深层次理解什么是进制
- 2019年,微信多开软件排行榜
热门文章
- Angular新建组件以及组件之间的调用
- Navicat控制mysql用户权限
- Tomcat下项目调整Log4J的console输出级别,减少输出信息
- Caused by: org.xml.sax.SAXParseException: cvc-elt.1: Cannot find the declaration of element 'beans'.
- 公告牌为什么有些是纸质,有些是电子的
- 科普丨营销人,还不知道行为触发的话你就 OUT 了!
- 神策 2021 数据驱动大会嘉宾阵容首曝 + 精彩观点前瞻
- 2017 数据驱动大会豪华议程出炉,早鸟票一周内全部售罄
- Python scrapy爬取京东,百度百科出现乱码,解决方案
- 挪动脑袋吃汉堡?AR版《贪吃蛇》帮你治好颈椎病