先上个 Swift 中的 demo
:Method Swizzlinggit
其实 Swift 中实现原理和 OC 基本一致,只是苹果爸爸再也不容许在 Swift 中使用+load()
和+initialize()
方法,这固然难不倒各类大神,那么我就作次农夫山泉。。。github
先抽取 swizzling 的实现到NSObject
的扩展当中:swift
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
。ide
咱们用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
被调用时候才调用。ui
OC 中+load()
方法会在类被装载时调用,确保须要用到的方法都是被 Swizzling 过的。Swift 中能够在AppDelegate
的init
方法中手动调用 swizzle 方法模拟+load()
实现。spa
class AppDelegate: UIResponder, UIApplicationDelegate {
override init() {
super.init()
UIViewController.swizzleViewDidLoad()
}
}
复制代码
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
。乍一看貌似没有问题(其实最开始我绞尽脑汁也没想清楚到底哪里不对。。。),可是为何各路大神都是用的第一种方式呢?网上有种说法:code
要先尝试添加原 selector 是为了作一层保护,由于若是这个类没有实现原始方法"originalSel" ,但其父类实现了,那 class_getInstanceMethod 会返回父类的方法。这样 method_exchangeImplementations 替换的是父类的那个方法,这固然不是你想要的。因此咱们先尝试添加 originalSel ,若是已经存在,再用 method_exchangeImplementations 把原方法的实现跟新的方法实现给交换掉。blog
其实这种说法已经算是比较明确问题所在了,可是愚笨的我仍是没有想通到底为什就“这固然不是你想要的”了呢。
又是一番绞尽脑汁。。。终于 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