收藏swift程序员

swift下使用runtime交换方法的实现

2018-05-08  本文已影响146人  小峰书

swift下使用runtime交换方法的实现

前言

  老的方式initialize现在已经不适用了,需要用新的方式代替。
思路: 定义一个启动的协议,在app完成启动的方法里把需要做method swizzle的类跑一边协议的方法

第一种

  1. Step One

     protocol SelfAware: class {
         static func awake()
     }
    
     class NothingToSeeHere {
    
          static func harmlessFunction() {
    
              let typeCount = Int(objc_getClassList(nil, 0))
              let types = UnsafeMutablePointer<AnyClass?>.allocate(capacity: typeCount)
              let autoreleasingTypes = AutoreleasingUnsafeMutablePointer<AnyClass?>(types)
                  objc_getClassList(autoreleasingTypes, Int32(typeCount))
              for index in 0 ..< typeCount { (types[index] as? SelfAware.Type)?.awake() }
              types.deallocate(capacity: typeCount)
    
          }
    
     }
    
  2. step two

    extension UIApplication {

         private static let runOnce: Void = {
             NothingToSeeHere.harmlessFunction()
         }()

        override open var next: UIResponder? {
             // Called before applicationDidFinishLaunching
             UIApplication.runOnce
             return super.next
        }

    }   
  1. step three
    遵循协议SelfAware,实现awake()

第二种(类似第一种)

  1. 创建一个swizzle注入的协议
public protocol SwizzlingInjection: class {
    static func inject()
}

  1. 创建swizzle helper
open class SwizzlingManager {
    
    //只会调用一次的方法
    private static let doOnce: Any? = {
        UIViewController.inject()
        return nil
    }()
    
    open static func enableInjection() {
        _ = SwizzlingManager.doOnce
    }
}

  1. 给UIApplication 创建分类调用那个一次方法
extension UIApplication{
    
    open override var next: UIResponder?{
        SwizzlingManager.enableInjection()
        return super.next
    }
}


  1. 在你需要的类中遵循注入协议
extension UIViewController: SwizzlingInjection{
    
        public static func inject() {
        //确保不是子类
        guard self === UIViewController.self else { return }
        
        DispatchQueue.once(token: "com.moglo.urmoji.UIViewController") {
            //do swizzle method
        }
    }
}

once只执行一次的方法

public extension DispatchQueue {
    
    private static var _onceTracker = [String]()
    
    public class func once(file: String = #file, function: String = #function, line: Int = #line, block:()->Void) {
        let token = file + ":" + function + ":" + String(line)
        once(token: token, block: block)
    }
    
    /**
     Executes a block of code, associated with a unique token, only once.  The code is thread safe and will
     only execute the code once even in the presence of multithreaded calls.
     
     - parameter token: A unique reverse DNS style name such as com.vectorform.<name> or a GUID
     - parameter block: Block to execute once
     */
    public class func once(token: String, block:()->Void) {
        objc_sync_enter(self)
        defer { objc_sync_exit(self) }
        
        
        if _onceTracker.contains(token) {
            return
        }
        
        _onceTracker.append(token)
        block()
    }
    
    
    //delay
    
    typealias Task = (_ cancel : Bool) -> Void
    
    @discardableResult
    static func delay(time : TimeInterval, task: @escaping () -> ()) -> Task? {
        
        func dispatch_later(block : @escaping () -> ()) {
            DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + time , execute: block)
        }
        
        var closure : (() -> ())? = task
        var result : Task?
        
        let delayedClosure : Task = {
            cancel in
            if let internalClosure = closure {
                if cancel == false {
                    DispatchQueue.main.async(execute: internalClosure)
                }
            }
            
            closure = nil
            result = nil
        }
        
        result = delayedClosure
        
        dispatch_later { () -> () in
            if let delayedClosure = result {
                delayedClosure(false)
            }
        }
        
        return result
    }
    
    static func cancel(task : Task?) {
        task?(true)
    }
    
}
上一篇下一篇

猜你喜欢

热点阅读