Method swizzling in iOS swift
What is method swizzling?
Method swizzling 是在运行时更改现有选择器的实现的过程。简单地说,我们可以在运行时更改方法的功能。
Note: 这是一个Objective-C运行时特性。
例如:如果你想跟踪添加到UserDefaults中的键和值,并在所有键之前添加一个前缀字符串,你可以用你自己的方法切换setValue:forKey方法的实现。因此,所有对setValue:forKey方法的调用都将被路由到新的选择器方法。您可以创建添加了前缀的新键字符串,并使用新键调用setValue:forKey方法的原始实现。你会困惑它是否会以无限循环结束。但事实并非如此。(注意:你可以通过在Objc或swift扩展中使用category来做同样的事情。只需创建一个新方法,并从这个新方法调用超级setValue:forKey方法。但是,如果导入的第三方库和框架直接使用setValue:forKey方法怎么办? 这些库不知道新的自定义方法,也不知道我们需要向键添加前缀。这就是method swizzling发挥作用的时候了). 我把这个例子从以下文章👇🏻👇🏻👇🏻👇🏻👇🏻👇🏻。
在继续之前请阅读Michael Mavris的文章。看看stackoverflow上关于如何在swift 3和4中做method swizzling的帖子。
How to do method swizzling in swift 4?
Well, its easy:
- Create a new method with your custom implementation for a method that you want to swizzle.
- Get the class representation.
- Get the old method selector reference.
- Get the new method selector reference.
- Ask the objective-C runtime to switch the selectors.
- Take a deep breath and relax.!! 😂.
Let’s swizzle!!
让我们为UIColor类的description方法创建一个交换方法。
如果你尝试打印一个UIColor对象,它会像这样打印RGBA值:
print(UIColor.red) // prints UIExtendedSRGBColorSpace 1 0 0 1
我们可以打印颜色,因为UIColor有一个描述方法,它将返回颜色的字符串表示。让我们试试这种方法。
import Foundation
import UIKit
public extension UIColor {
@objc func colorDescription() -> String {
return "Printing rainbow colours."
}
private static let swizzleDesriptionImplementation: Void = {
let instance: UIColor = UIColor.red
let aClass: AnyClass! = object_getClass(instance)
let originalMethod = class_getInstanceMethod(aClass, #selector(description))
let swizzledMethod = class_getInstanceMethod(aClass, #selector(colorDescription))
if let originalMethod = originalMethod, let swizzledMethod = swizzledMethod {
// switch implementation..
method_exchangeImplementations(originalMethod, swizzledMethod)
}
}()
public static func swizzleDesription() {
_ = self.swizzleDesriptionImplementation
}
}
我认为上面的代码很容易解释。在我的视图控制器中,我添加了以下代码。
override func viewDidLoad() {
super.viewDidLoad()
print(UIColor.red)
print(UIColor.green)
UIColor.swizzleDesription()
print(“\nswizzled\n”)
print(UIColor.red)
print(UIColor.red)
UIColor.swizzleDesription()
print(“\nTrying to swizzle again\n”)
print(UIColor.red)
print(UIColor.red)
}
Output
UIExtendedSRGBColorSpace 1 0 0 1
UIExtendedSRGBColorSpace 0 1 0 1
swizzled
Printing rainbow colours.
Printing rainbow colours.
Trying to swizzle again
Printing rainbow colours.
Printing rainbow colours.
Swift的静态成员是隐式惰性(lazy)的。这就是为什么swizzledescriptionimplementation没有被再次调用,也没有再次发生swizzledescriptionimplementation的原因。 将修改方法的所有操作封闭在计算的全局常量的惰性初始化块中,可以确保该过程只执行一次(因为这些变量或常量的初始化在后台使用dispatch_once)。
Drawbacks of method swizzling
-
如果您正在使用任何标准类方法进行交换,那么最好确保交换成功,并调用新的交换方法。 如果你使用的框架(例如Firebase)使用了交换,请确保你没有混合他们已经交换的一些方法。如果交换发生多次,要么您的代码不能工作,要么firebase(或任何其他框架混合相同的方法)不能工作。
-
当更新的iOS版本发布时,交换有可能失败。你可能每次都要反复核对。
-
如果你发行的框架(例如一些分析框架)被数百款应用所使用,那么最好不要在这种情况下使用交换功能。如果你想使用交换,请确保应用程序开发人员知道框架所做的交换。确保将其添加到文档中。
-
在子类中进行混合将是一个头痛的事。可能会发生意想不到的事情。