關於Swift4.0 Method Swizzling(iOS的hook機制)使用

關於Method Swizzling 原理什麼的有不少帖子講述的已經很清楚這裏再也不贅述,swift

這裏僅僅處理Method Swizzling 在swift4.0中的使用方法api

由於Swift自己對Runtime的支持並非很到位,尤爲是Method-Swizzling在OC中很是經常使用,可是到Swift後發現load方法不見了進而須要用initialize代替,甚至到了Swift4中直接取消了initialize方法。所以須要本身初始化app

解決方案須要在appdelegate 添加這一行代碼
 UIViewController.initializeMethod()async

/**
 須要在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
        }
        //DispatchQueue函數保證代碼只被執行一次,防止又被交換回去致使得不到想要的效果
        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)
            //在進行 Swizzling 的時候,須要用 class_addMethod 先進行判斷一下原有類中是否有要替換方法的實現
            let didAddMethod: Bool = class_addMethod(self, originalSelector, method_getImplementation(swizzledMethod!), method_getTypeEncoding(swizzledMethod!))
            //若是 class_addMethod 返回 yes,說明當前類中沒有要替換方法的實現,因此須要在父類中查找,這時候就用到 method_getImplemetation 去獲取 class_getInstanceMethod 裏面的方法實現,而後再進行 class_replaceMethod 來實現 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)
            //在進行 Swizzling 的時候,須要用 class_addMethod 先進行判斷一下原有類中是否有要替換方法的實現
            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) {
        //須要注入的代碼寫在此處
        self.swizzled_viewWillAppear(animated: animated)
       DDLOG(message: "\(NSStringFromClass(classForCoder))--Appear")

    }
    @objc func swizzled_viewWillDisappear(animated: Bool) {
        //須要注入的代碼寫在此處
        self.swizzled_viewWillDisappear(animated: animated)
       DDLOG(message: "\(NSStringFromClass(classForCoder))--Disappear")

    }
}

因爲swift 沒有DispatchQueue.once 方法 因此手動擴展了一個 方便使用函數

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)
    }
}
相關文章
相關標籤/搜索