首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >iOS runtime swift swizzling

iOS runtime swift swizzling

作者头像
用户6094182
发布2019-08-23 17:56:24
1.4K0
发布2019-08-23 17:56:24
举报
文章被收录于专栏:joealzhoujoealzhou

iOS runtime swift中的Swizzling方法交换

背景知识

Swift 是一种强类型语言。即默认类型是安全的静态类型。纯Swift类的函数调用已经不再是OC的运行时发送消息,而是类似于C++的vtable,在编译时就确定了调用哪个函数,所以没法通过runtime获取方法,属性。Swift中的动态性可以通过OC运行时来获得,动态性最常用的就是方法替换(Method Swizzling)。

swift动态修饰符
  • @objc 将Swift函数暴露给OC运行时,但是它仍然不能保证完全动态,编译器会尝试去对它做静态优化
  • dynamic 动态功能修饰符,它能保证函数,属性可以获得动态性
Swizzling实现地方
  • +load()
  • +initialize()

这里我们最好是选择+load函数里面实现方法交换。+load方法是在类加载的时候就会调用,而+initialize方法是在给类发送第一个消息之前再调用,相当于懒加载一样。

创建

class Person: NSObject {

    @objc dynamic func test1() {
        debugPrint("test 1")
    }
    
}

实现方法一

extension UIApplication {
    private static let runOnce: Void = {
        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()
    }()
    
    open override var next: UIResponder? {
        UIApplication.runOnce
        return super.next
    }
}

/// swizzling协议 在需要交换方法的类中遵循此协议,实现方法awake
protocol SelfAware: class {
    static func awake()
}

实现:Person类遵从SelfAware协议并实现方法awake,在awake方法内实现runtime方法交换。

extension Person: SelfAware {
    
    /// SwlfAware实现方法
    static func awake() {
        changeFunc()
    }
    
    static func changeFunc() {
        if let method1 = class_getInstanceMethod(Person.self, #selector(Person.test1)),
            let method2 = class_getInstanceMethod(Person.self, #selector(Person.test2)) {
            let didAddMethod = class_addMethod(Person.self, #selector(Person.test1), method_getImplementation(method2), method_getTypeEncoding(method2))
            if didAddMethod {//不存在方法test1
                class_replaceMethod(Person.self, #selector(Person.test2), method_getImplementation(method1), method_getTypeEncoding(method1))
            } else {//存在test1方法
                method_exchangeImplementations(method1, method2)
            }
            
        }
    }
    
    @objc func test2() {
        debugPrint("test 2")
    }
    
}

实现方法二

第二种方法类似于上一种,也是靠协议实现。

/// swizzling协议
protocol SwizzlingProtocol {
    static func inject()
}

/// 管理类
class SwizzlingManager {
    
    /// 只会调用一次
    static let doOnece: Void = {
        //以后别的类也在此处添加XX.inject()
       Person.inject()
    }()
    
    func enableInjection() {
        SwizzlingManager.doOnece
    }
}

extension UIApplication {
    open override var next: UIResponder? {
        SwizzlingManager.enableInjection()
        return super.next
    }
}

实现:遵从协议SwizzlingProtocol实现方法inject。然后在SwizzlingManager doOnece常量里面注册。

extension Person: SwizzlingProtocol {
    
    /// SwizzlingProtocol实现方法
    static func inject() {
        DispatchQueue.once {
            changeFunc()
        }
    }
    
    static func changeFunc() {
        if let method1 = class_getInstanceMethod(Person.self, #selector(Person.test1)),
            let method2 = class_getInstanceMethod(Person.self, #selector(Person.test2)) {
            let didAddMethod = class_addMethod(Person.self, #selector(Person.test1), method_getImplementation(method2), method_getTypeEncoding(method2))
            if didAddMethod {
                class_replaceMethod(Person.self, #selector(Person.test2), method_getImplementation(method1), method_getTypeEncoding(method1))
            } else {
                method_exchangeImplementations(method1, method2)
            }
            
        }
    }
    
    @objc func test2() {
        debugPrint("test 2")
    }
    
}

在swift3.0后DispatchQueue.once就已经被取消了,我们需要另外实现:

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)
    }
    
    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()
    }
    
}
本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2019.04.08 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • iOS runtime swift中的Swizzling方法交换
    • 背景知识
      • swift动态修饰符
      • Swizzling实现地方
    • 创建
      • 实现方法一
        • 实现方法二
        领券
        问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档