There are many posts about the principle of Method Swizzling. It's clear that we won't repeat it here,
Only the use of Method Swizzling in swift 4.0 is dealt with here
Because the Runtime support of Swift itself is not very in place, especially the method swizzling is very common in OC, but after Swift, it is found that the load method is missing and needs to be replaced by initialize. Even in Swift4, it directly cancels the initialize method. So you need to initialize yourself
The solution needs to add this line of code in appdelegate
UIViewController.initializeMethod()
/** You need to add this line of code in appdelegate UIViewController.initializeMethod() */
private let onceToken = "Method Swizzling" extension UIViewController { public class func initializeMethod() { // Make sure This isn't a subclass of UIViewController, So that It applies to all UIViewController childs if self != UIViewController.self { return } //The DispatchQueue function ensures that the code is executed only once, preventing it from being swapped back to the desired effect DispatchQueue.once(token: onceToken) { let originalSelector = #selector(UIViewController.viewWillAppear(_:)) let swizzledSelector = #selector(UIViewController.swizzled_viewWillAppear(animated:)) let originalMethod = class_getInstanceMethod(self, originalSelector) let swizzledMethod = class_getInstanceMethod(self, swizzledSelector) //When Swizzling, it is necessary to use class "Addmethod" to determine whether there is an implementation of the method to be replaced in the original class let didAddMethod: Bool = class_addMethod(self, originalSelector, method_getImplementation(swizzledMethod!), method_getTypeEncoding(swizzledMethod!)) //If the class add method returns yes, it means that there is no implementation of the method to replace in the current class, so you need to find it in the parent class. At this time, you need to use the method getimplement method to get the method implementation in the class getinstancemethod, and then class replace method to implement Swizzing if didAddMethod { class_replaceMethod(self, swizzledSelector, method_getImplementation(originalMethod!), method_getTypeEncoding(originalMethod!)) } else { method_exchangeImplementations(originalMethod!, swizzledMethod!) } let originalSelector1 = #selector(UIViewController.viewWillDisappear(_:)) let swizzledSelector1 = #selector(UIViewController.swizzled_viewWillDisappear(animated:)) let originalMethod1 = class_getInstanceMethod(self, originalSelector1) let swizzledMethod1 = class_getInstanceMethod(self, swizzledSelector1) //When Swizzling, it is necessary to use class "Addmethod" to determine whether there is an implementation of the method to be replaced in the original class let didAddMethod1: Bool = class_addMethod(self, originalSelector1, method_getImplementation(swizzledMethod1!), method_getTypeEncoding(swizzledMethod1!)) if didAddMethod1 { class_replaceMethod(self, swizzledSelector1, method_getImplementation(originalMethod1!), method_getTypeEncoding(originalMethod1!)) } else { method_exchangeImplementations(originalMethod1!, swizzledMethod1!) } } } @objc func swizzled_viewWillAppear(animated: Bool) { //The code to be injected is written here self.swizzled_viewWillAppear(animated: animated) DDLOG(message: "\(NSStringFromClass(classForCoder))--Appear") } @objc func swizzled_viewWillDisappear(animated: Bool) { //The code to be injected is written here self.swizzled_viewWillDisappear(animated: animated) DDLOG(message: "\(NSStringFromClass(classForCoder))--Disappear") } }
Since swift does not have the DispatchQueue.once method, it manually extends a convenient way to use
extension DispatchQueue { private static var _onceTracker = [String]() public class func once(token: String, block: () -> ()) { objc_sync_enter(self) defer { objc_sync_exit(self) } if _onceTracker.contains(token) { return } _onceTracker.append(token) block() } func async(block: @escaping ()->()) { self.async(execute: block) } func after(time: DispatchTime, block: @escaping ()->()) { self.asyncAfter(deadline: time, execute: block) } }