如何在 Swift 中调配 init
How to swizzle init in Swift
我正在关注 Swift & the Objective-C Runtime,它适用于常规方法。
我喜欢调配 init 方法,根据我的理解,init 就像一个 class 方法。所以我尝试将 init 作为实例和 class 方法调配。但是好像不行
我可以使用 Objective C 让它工作,只是想知道如何让它在 Swift
中工作
摘自我的gist
dispatch_once(&Static.token) {
let originalSelector = Selector("init:source:destination:")
let swizzledSelector = Selector("ftg_init:source:destination:")
let originalMethod = class_getClassMethod(self, originalSelector)
let swizzledMethod = class_getClassMethod(self, swizzledSelector)
let didAddMethod = class_addMethod(self, originalSelector, method_getImplementation(swizzledMethod), method_getTypeEncoding(swizzledMethod))
if didAddMethod {
class_replaceMethod(self, swizzledSelector, method_getImplementation(originalMethod), method_getTypeEncoding(originalMethod))
} else {
method_exchangeImplementations(originalMethod, swizzledMethod);
}
}
为方法创建选择器时,您应该基于 Obj C 方法签名,因为调配是使用 Obj C 运行时完成的。
所以原来的选择器应该是
initWithIdentifier:source:destination:
现在这有点奇怪了,因为你的 init 方法被定义为第一个参数上的标签是必需的(通过 identifier
两次),你想使用的选择器实际上是
ftg_initWithIdentifier:source:destination:
我能找到的唯一 documentation 是关于从 Obj C 到 Swift 的翻译,但看起来从 Swift 到 Obj C 的情况正好相反。
接下来,init...
是一个实例方法,因此您需要进行两处更改。您需要将 class_getClassMethod
更改为 class_getInstanceMethod
,并且需要从 ft_init...
方法中删除 class
。
所以当一切都说完了,你的代码应该是这样的(这对我有用)
dispatch_once(&Static.token) {
let originalSelector = Selector("initWithIdentifier:source:destination:")
let swizzledSelector = Selector("ftg_initWithIdentifier:source:destination:")
let originalMethod = class_getInstanceMethod(self, originalSelector)
let swizzledMethod = class_getInstanceMethod(self, swizzledSelector)
let didAddMethod = class_addMethod(self, originalSelector, method_getImplementation(swizzledMethod), method_getTypeEncoding(swizzledMethod))
if didAddMethod {
class_replaceMethod(self, swizzledSelector, method_getImplementation(originalMethod), method_getTypeEncoding(originalMethod))
} else {
method_exchangeImplementations(originalMethod, swizzledMethod);
}
}
func ftg_init(identifier identifier: String!,
source: UIViewController,
destination: UIViewController) -> UIStoryboardSegue {
return ftg_init(identifier: identifier,
source: source,
destination: destination.ftg_resolve())
}
这是 Swift 4.2 中 UIImage 初始化器的实现方式:
@objc static func ftg_imageNamed(_ name: String) -> UIImage? {
...
}
private static func swizzleInitImplementation() {
let originalSelector = #selector(UIImage.init(named:))
let swizzledSelector = #selector(UIImage.ftg_imageNamed(_:))
guard let originalMethod = class_getClassMethod(self, originalSelector), let swizzledMethod = class_getClassMethod(self, swizzledSelector) else {
assertionFailure("The methods are not found!")
return
}
let didAddMethod = class_addMethod(self, originalSelector, method_getImplementation(swizzledMethod), method_getTypeEncoding(swizzledMethod))
if didAddMethod {
class_replaceMethod(self, swizzledSelector, method_getImplementation(originalMethod), method_getTypeEncoding(originalMethod))
} else {
method_exchangeImplementations(originalMethod, swizzledMethod);
}
}
我正在关注 Swift & the Objective-C Runtime,它适用于常规方法。
我喜欢调配 init 方法,根据我的理解,init 就像一个 class 方法。所以我尝试将 init 作为实例和 class 方法调配。但是好像不行
我可以使用 Objective C 让它工作,只是想知道如何让它在 Swift
中工作摘自我的gist
dispatch_once(&Static.token) {
let originalSelector = Selector("init:source:destination:")
let swizzledSelector = Selector("ftg_init:source:destination:")
let originalMethod = class_getClassMethod(self, originalSelector)
let swizzledMethod = class_getClassMethod(self, swizzledSelector)
let didAddMethod = class_addMethod(self, originalSelector, method_getImplementation(swizzledMethod), method_getTypeEncoding(swizzledMethod))
if didAddMethod {
class_replaceMethod(self, swizzledSelector, method_getImplementation(originalMethod), method_getTypeEncoding(originalMethod))
} else {
method_exchangeImplementations(originalMethod, swizzledMethod);
}
}
为方法创建选择器时,您应该基于 Obj C 方法签名,因为调配是使用 Obj C 运行时完成的。
所以原来的选择器应该是
initWithIdentifier:source:destination:
现在这有点奇怪了,因为你的 init 方法被定义为第一个参数上的标签是必需的(通过 identifier
两次),你想使用的选择器实际上是
ftg_initWithIdentifier:source:destination:
我能找到的唯一 documentation 是关于从 Obj C 到 Swift 的翻译,但看起来从 Swift 到 Obj C 的情况正好相反。
接下来,init...
是一个实例方法,因此您需要进行两处更改。您需要将 class_getClassMethod
更改为 class_getInstanceMethod
,并且需要从 ft_init...
方法中删除 class
。
所以当一切都说完了,你的代码应该是这样的(这对我有用)
dispatch_once(&Static.token) {
let originalSelector = Selector("initWithIdentifier:source:destination:")
let swizzledSelector = Selector("ftg_initWithIdentifier:source:destination:")
let originalMethod = class_getInstanceMethod(self, originalSelector)
let swizzledMethod = class_getInstanceMethod(self, swizzledSelector)
let didAddMethod = class_addMethod(self, originalSelector, method_getImplementation(swizzledMethod), method_getTypeEncoding(swizzledMethod))
if didAddMethod {
class_replaceMethod(self, swizzledSelector, method_getImplementation(originalMethod), method_getTypeEncoding(originalMethod))
} else {
method_exchangeImplementations(originalMethod, swizzledMethod);
}
}
func ftg_init(identifier identifier: String!,
source: UIViewController,
destination: UIViewController) -> UIStoryboardSegue {
return ftg_init(identifier: identifier,
source: source,
destination: destination.ftg_resolve())
}
这是 Swift 4.2 中 UIImage 初始化器的实现方式:
@objc static func ftg_imageNamed(_ name: String) -> UIImage? {
...
}
private static func swizzleInitImplementation() {
let originalSelector = #selector(UIImage.init(named:))
let swizzledSelector = #selector(UIImage.ftg_imageNamed(_:))
guard let originalMethod = class_getClassMethod(self, originalSelector), let swizzledMethod = class_getClassMethod(self, swizzledSelector) else {
assertionFailure("The methods are not found!")
return
}
let didAddMethod = class_addMethod(self, originalSelector, method_getImplementation(swizzledMethod), method_getTypeEncoding(swizzledMethod))
if didAddMethod {
class_replaceMethod(self, swizzledSelector, method_getImplementation(originalMethod), method_getTypeEncoding(originalMethod))
} else {
method_exchangeImplementations(originalMethod, swizzledMethod);
}
}