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方法是在给类发送第一个消息之前再调用,相当于懒加载一样。
创建
代码语言:javascript复制class Person: NSObject {
@objc dynamic func test1() {
debugPrint("test 1")
}
}
实现方法一
代码语言:javascript复制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方法交换。
代码语言:javascript复制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")
}
}
实现方法二
第二种方法类似于上一种,也是靠协议实现。
代码语言:javascript复制/// 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常量里面注册。
代码语言:javascript复制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就已经被取消了,我们需要另外实现:
代码语言:javascript复制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()
}
}