交换方法Method Swizzling[swift]

栏目: Swift · 发布时间: 6年前

内容简介:1. dispatch_once替代方案OC中用来保证代码块只执行一次的dispatch_once在swfit中已经被废弃了,取而代之的是使用static let,let本身就带有线程安全性质的.例如单例的实现.

1. dispatch_once替代方案

OC中用来保证代码块只执行一次的dispatch_once在swfit中已经被废弃了,取而代之的是使用static let,let本身就带有线程安全性质的.

例如单例的实现.

final public class MySingleton {
    static let shared = MySingleton()
    private init() {}
}

但如果我们不想定义常量,需要某个代码块执行一次呢?

private lazy var takeOnceTime: Void = {
    // 代码块...
}()

_ = takeOnceTime

定义一个懒加载的变量,防止在初始化的时候被执行.后面加一个void,为了在_ = takeOnceTime赋值时不耗性能,返回一个Void类型.

lazy var改为static let也可以,为了使用方便,我们用一个类方法封装下

class ClassName {
    private static let takeOnceTime: Void = {
        // 代码块...
    }()
    static func takeOnceTimeFunc() {
        ClassName.takeOnceTime
    }
}

// 使用
ClassName.takeOnceTimeFunc()

这样就可以做到和dispatch_once一样的效果了.

2. 被废弃的+load()和+initialize()

我们都知道OC中两个方法+load()和+initialize().

+load(): app启动的时候会加载所有的类,此时就会调用每个类的load方法.

+initialize(): 第一次初始化这个类的时候会被调用.

然而在目前的swift版本中这两个方法都不可用了,那现在我们要在这个阶段搞事情该怎么做? 例如method swizzling.

JORDAN SMITH大神给出了一种很巧解决方案.UIApplication有一个next属性,它会在applicationDidFinishLaunching之前被调用,这个时候通过runtime获取到所有类的列表,然后向所有遵循SelfAware协议的类发送消息.

extension UIApplication {
    private static let runOnce: Void = {
        NothingToSeeHere.harmlessFunction()
    }()
    override open var next: UIResponder? {
        // Called before applicationDidFinishLaunching
        UIApplication.runOnce
        return super.next
    }
}

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()
    }
}
 </anyclass>
</anyclass>

之后任何遵守SelfAware协议实现的+awake()方法在这个阶段都会被调用.

3. 交换方法 Method Swizzling

黑魔法Method Swizzling在swift中实现的两个困难点

  • swizzling 应该保证只会执行一次.

  • swizzling 应该在加载所有类的时候调用.

分别在上面给出了解决方案.

下面给出了两个示例供参考:

protocol SelfAware: class {
    static func awake()
    static func swizzlingForClass(_ forClass: AnyClass, originalSelector: Selector, swizzledSelector: Selector)
}
extension SelfAware {
    
    static func swizzlingForClass(_ forClass: AnyClass, originalSelector: Selector, swizzledSelector: Selector) {
        let originalMethod = class_getInstanceMethod(forClass, originalSelector)
        let swizzledMethod = class_getInstanceMethod(forClass, swizzledSelector)
        guard (originalMethod != nil && swizzledMethod != nil) else {
            return
        }
        if class_addMethod(forClass, originalSelector, method_getImplementation(swizzledMethod!), method_getTypeEncoding(swizzledMethod!)) {
            class_replaceMethod(forClass, swizzledSelector, method_getImplementation(originalMethod!), method_getTypeEncoding(originalMethod!))
        } else {
            method_exchangeImplementations(originalMethod!, swizzledMethod!)
        }
    }
}
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()
    }
}
extension UIApplication {
    private static let runOnce: Void = {
        NothingToSeeHere.harmlessFunction()
    }()
    override open var next: UIResponder? {
        UIApplication.runOnce
        return super.next
    }
}
 </anyclass>
</anyclass>

在SelfAware的extension中为swizzlingForClass做了默认实现,相当于一层封装.

1. 给按钮添加点击计数

extension UIButton: SelfAware {
    static func awake() {
        UIButton.takeOnceTime
    }
    private static let takeOnceTime: Void = {
        let originalSelector = #selector(sendAction)
        let swizzledSelector = #selector(xxx_sendAction(action:to:forEvent:))
        
        swizzlingForClass(UIButton.self, originalSelector: originalSelector, swizzledSelector: swizzledSelector)
    }()
    
    @objc public func xxx_sendAction(action: Selector, to: AnyObject!, forEvent: UIEvent!) {
        struct xxx_buttonTapCounter {
            static var count: Int = 0
        }
        xxx_buttonTapCounter.count += 1
        print(xxx_buttonTapCounter.count)
        xxx_sendAction(action: action, to: to, forEvent: forEvent)
    }
}

2. 替换控制器的viewWillAppear方法

extension UIViewController: SelfAware {
    static func awake() {
        swizzleMethod
    }
    private static let swizzleMethod: Void = {
        let originalSelector = #selector(viewWillAppear(_:))
        let swizzledSelector = #selector(swizzled_viewWillAppear(_:))
        
        swizzlingForClass(UIViewController.self, originalSelector: originalSelector, swizzledSelector: swizzledSelector)
    }()
    
    @objc func swizzled_viewWillAppear(_ animated: Bool) {
        swizzled_viewWillAppear(animated)
        print("swizzled_viewWillAppear")
    }
}

本文收录于 SwiftTips

如有疑问,欢迎留言 :-D

作者:Dariel

链接:https://www.jianshu.com/p/23ea81be5cc2


以上所述就是小编给大家介绍的《交换方法Method Swizzling[swift]》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

C语言程序开发范例宝典

C语言程序开发范例宝典

2010-1 / 59.00元

《C语言程序开发范例宝典》全面介绍了应用C语言进行开发的各种技术和技巧,全书共分12章,内容包括基础知识、指针、数据结构、算法、数学应用、文件操作、库函数应用、图形图像、系统调用、加解密与安全性、游戏、综合应用等。全书共提供300个实例,每个实例都突出了其实用性。 《C语言程序开发范例宝典》既可作为C程序的初学者学习用书,也可作为程序开发人员、相关培训机构老师和学生的参考用书。一起来看看 《C语言程序开发范例宝典》 这本书的介绍吧!

HTML 压缩/解压工具
HTML 压缩/解压工具

在线压缩/解压 HTML 代码

Base64 编码/解码
Base64 编码/解码

Base64 编码/解码

Markdown 在线编辑器
Markdown 在线编辑器

Markdown 在线编辑器