How can I implement method swizzling in Swift 3.0 ?
如何在Swift 3.0中实现方法swizzle ?
I've read nshipster article about it, but in this code's chunk
我读过nshipster的文章,但在这段代码中。
struct Static {
static var token: dispatch_once_t = 0
}
the compiler gives me an error
编译器给我一个错误
dispatch_once_t is unavailable in Swift: Use lazily initialized globals instead
dispatch_once_t在Swift中不可用:使用惰性初始化的全局变量
3 个解决方案
#1
48
First of all dispatch_once_t
is unavailable in Swift 3.0. You can choose from two alternatives:
首先,dispatch_once_t在Swift 3.0中不可用。你可以从两种选择中选择:
-
Global variable
全局变量
-
Static property of
struct
,enum
orclass
结构、枚举或类的静态属性
For more details, see that Whither dispatch_once in Swift 3
有关更多细节,请参见Swift 3中的dispatch_once
For different purposes you must use different implementation of swizzling
- Swizzling CocoaTouch class, for example UIViewController;
- swizzle CocoaTouch类,例如UIViewController;
- Swizzling custom Swift class;
- 纵酒自定义快速类;
Swizzling CocoaTouch class
example swizzling viewWillAppear(_:)
of UIViewController
using global variable
使用全局变量的UIViewController的swizzle viewWillAppear(_:)示例
private let swizzling: (UIViewController.Type) -> () = { viewController in
let originalSelector = #selector(viewController.viewWillAppear(_:))
let swizzledSelector = #selector(viewController.proj_viewWillAppear(animated:))
let originalMethod = class_getInstanceMethod(viewController, originalSelector)
let swizzledMethod = class_getInstanceMethod(viewController, swizzledSelector)
method_exchangeImplementations(originalMethod, swizzledMethod) }
extension UIViewController {
open override class func initialize() {
// make sure this isn't a subclass
guard self === UIViewController.self else { return }
swizzling(self)
}
// MARK: - Method Swizzling
func proj_viewWillAppear(animated: Bool) {
self.proj_viewWillAppear(animated: animated)
let viewControllerName = NSStringFromClass(type(of: self))
print("viewWillAppear: \(viewControllerName)")
}
}
Swizzling custom Swift class
To use method swizzling with your Swift classes there are two requirements that you must comply with (for more details):
要在Swift类中使用方法swizzle,您必须遵守两个要求(更多细节):
- The class containing the methods to be swizzled must extend
NSObject
- 包含要刷新的方法的类必须扩展NSObject
- The methods you want to swizzle must have the
dynamic
attribute - 要swizzle的方法必须具有dynamic属性
And example swizzling method of custom Swift base class Person
并举例自定义Swift基类Person的swizzle方法
class Person: NSObject {
var name = "Person"
dynamic func foo(_ bar: Bool) {
print("Person.foo")
}
}
class Programmer: Person {
override func foo(_ bar: Bool) {
super.foo(bar)
print("Programmer.foo")
}
}
private let swizzling: (Person.Type) -> () = { person in
let originalSelector = #selector(person.foo(_:))
let swizzledSelector = #selector(person.proj_foo(_:))
let originalMethod = class_getInstanceMethod(person, originalSelector)
let swizzledMethod = class_getInstanceMethod(person, swizzledSelector)
method_exchangeImplementations(originalMethod, swizzledMethod)
}
extension Person {
open override class func initialize() {
// make sure this isn't a subclass
guard self === Person.self else { return }
swizzling(self)
}
// MARK: - Method Swizzling
func proj_foo(_ bar: Bool) {
self.proj_foo(bar)
let className = NSStringFromClass(type(of: self))
print("class: \(className)")
}
}
#2
20
@TikhonovAlexander: Great answer
@TikhonovAlexander:伟大的回答
I modified the swizzler to take both selectors and made it more generic.
我修改了swizzler,让两个选择器,使它更通用。
Swift 3
斯威夫特3
private let swizzling: (AnyClass, Selector, Selector) -> () = { forClass, originalSelector, swizzledSelector in
let originalMethod = class_getInstanceMethod(forClass, originalSelector)
let swizzledMethod = class_getInstanceMethod(forClass, swizzledSelector)
method_exchangeImplementations(originalMethod, swizzledMethod)
}
// perform swizzling in initialize()
extension UIView {
open override class func initialize() {
// make sure this isn't a subclass
guard self === UIView.self else { return }
let originalSelector = #selector(layoutSubviews)
let swizzledSelector = #selector(swizzled_layoutSubviews)
swizzling(self, originalSelector, swizzledSelector)
}
func swizzled_layoutSubviews() {
swizzled_layoutSubviews()
print("swizzled_layoutSubviews")
}
}
Swift 4
斯威夫特4
private let swizzling: (AnyClass, Selector, Selector) -> () = { forClass, originalSelector, swizzledSelector in
let originalMethod = class_getInstanceMethod(forClass, originalSelector)
let swizzledMethod = class_getInstanceMethod(forClass, swizzledSelector)
method_exchangeImplementations(originalMethod, swizzledMethod)
}
extension UIView {
static let classInit: Void = {
let originalSelector = #selector(layoutSubviews)
let swizzledSelector = #selector(swizzled_layoutSubviews)
swizzling(UIView.self, originalSelector, swizzledSelector)
}()
func swizzled_layoutSubviews() {
swizzled_layoutSubviews()
print("swizzled_layoutSubviews")
}
}
// perform swizzling in AppDelegate.init()
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
override init() {
super.init()
UIView.classInit
}
}
#3
2
swizzling in playground
狂饮在操场上
class TestSwizzling : NSObject {
dynamic func methodOne()->Int{
return 1
}
}
extension TestSwizzling {
//In Objective-C you'd perform the swizzling in load(),
//but this method is not permitted in Swift
func initialize1()
{
let i: () -> () = {
let originalSelector = #selector(TestSwizzling.methodOne)
let swizzledSelector = #selector(TestSwizzling.methodTwo)
let originalMethod = class_getInstanceMethod(TestSwizzling.self, originalSelector);
let swizzledMethod = class_getInstanceMethod(TestSwizzling.self, swizzledSelector)
method_exchangeImplementations(originalMethod, swizzledMethod)
print("swizzled")
}
i()
}
func methodTwo()->Int{
// It will not be a recursive call anymore after the swizzling
return 4
}
}
var c = TestSwizzling()
c.initialize1()
print(c.methodOne())
print(c.methodTwo())
output: 4 1
输出:4 1
#1
48
First of all dispatch_once_t
is unavailable in Swift 3.0. You can choose from two alternatives:
首先,dispatch_once_t在Swift 3.0中不可用。你可以从两种选择中选择:
-
Global variable
全局变量
-
Static property of
struct
,enum
orclass
结构、枚举或类的静态属性
For more details, see that Whither dispatch_once in Swift 3
有关更多细节,请参见Swift 3中的dispatch_once
For different purposes you must use different implementation of swizzling
- Swizzling CocoaTouch class, for example UIViewController;
- swizzle CocoaTouch类,例如UIViewController;
- Swizzling custom Swift class;
- 纵酒自定义快速类;
Swizzling CocoaTouch class
example swizzling viewWillAppear(_:)
of UIViewController
using global variable
使用全局变量的UIViewController的swizzle viewWillAppear(_:)示例
private let swizzling: (UIViewController.Type) -> () = { viewController in
let originalSelector = #selector(viewController.viewWillAppear(_:))
let swizzledSelector = #selector(viewController.proj_viewWillAppear(animated:))
let originalMethod = class_getInstanceMethod(viewController, originalSelector)
let swizzledMethod = class_getInstanceMethod(viewController, swizzledSelector)
method_exchangeImplementations(originalMethod, swizzledMethod) }
extension UIViewController {
open override class func initialize() {
// make sure this isn't a subclass
guard self === UIViewController.self else { return }
swizzling(self)
}
// MARK: - Method Swizzling
func proj_viewWillAppear(animated: Bool) {
self.proj_viewWillAppear(animated: animated)
let viewControllerName = NSStringFromClass(type(of: self))
print("viewWillAppear: \(viewControllerName)")
}
}
Swizzling custom Swift class
To use method swizzling with your Swift classes there are two requirements that you must comply with (for more details):
要在Swift类中使用方法swizzle,您必须遵守两个要求(更多细节):
- The class containing the methods to be swizzled must extend
NSObject
- 包含要刷新的方法的类必须扩展NSObject
- The methods you want to swizzle must have the
dynamic
attribute - 要swizzle的方法必须具有dynamic属性
And example swizzling method of custom Swift base class Person
并举例自定义Swift基类Person的swizzle方法
class Person: NSObject {
var name = "Person"
dynamic func foo(_ bar: Bool) {
print("Person.foo")
}
}
class Programmer: Person {
override func foo(_ bar: Bool) {
super.foo(bar)
print("Programmer.foo")
}
}
private let swizzling: (Person.Type) -> () = { person in
let originalSelector = #selector(person.foo(_:))
let swizzledSelector = #selector(person.proj_foo(_:))
let originalMethod = class_getInstanceMethod(person, originalSelector)
let swizzledMethod = class_getInstanceMethod(person, swizzledSelector)
method_exchangeImplementations(originalMethod, swizzledMethod)
}
extension Person {
open override class func initialize() {
// make sure this isn't a subclass
guard self === Person.self else { return }
swizzling(self)
}
// MARK: - Method Swizzling
func proj_foo(_ bar: Bool) {
self.proj_foo(bar)
let className = NSStringFromClass(type(of: self))
print("class: \(className)")
}
}
#2
20
@TikhonovAlexander: Great answer
@TikhonovAlexander:伟大的回答
I modified the swizzler to take both selectors and made it more generic.
我修改了swizzler,让两个选择器,使它更通用。
Swift 3
斯威夫特3
private let swizzling: (AnyClass, Selector, Selector) -> () = { forClass, originalSelector, swizzledSelector in
let originalMethod = class_getInstanceMethod(forClass, originalSelector)
let swizzledMethod = class_getInstanceMethod(forClass, swizzledSelector)
method_exchangeImplementations(originalMethod, swizzledMethod)
}
// perform swizzling in initialize()
extension UIView {
open override class func initialize() {
// make sure this isn't a subclass
guard self === UIView.self else { return }
let originalSelector = #selector(layoutSubviews)
let swizzledSelector = #selector(swizzled_layoutSubviews)
swizzling(self, originalSelector, swizzledSelector)
}
func swizzled_layoutSubviews() {
swizzled_layoutSubviews()
print("swizzled_layoutSubviews")
}
}
Swift 4
斯威夫特4
private let swizzling: (AnyClass, Selector, Selector) -> () = { forClass, originalSelector, swizzledSelector in
let originalMethod = class_getInstanceMethod(forClass, originalSelector)
let swizzledMethod = class_getInstanceMethod(forClass, swizzledSelector)
method_exchangeImplementations(originalMethod, swizzledMethod)
}
extension UIView {
static let classInit: Void = {
let originalSelector = #selector(layoutSubviews)
let swizzledSelector = #selector(swizzled_layoutSubviews)
swizzling(UIView.self, originalSelector, swizzledSelector)
}()
func swizzled_layoutSubviews() {
swizzled_layoutSubviews()
print("swizzled_layoutSubviews")
}
}
// perform swizzling in AppDelegate.init()
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
override init() {
super.init()
UIView.classInit
}
}
#3
2
swizzling in playground
狂饮在操场上
class TestSwizzling : NSObject {
dynamic func methodOne()->Int{
return 1
}
}
extension TestSwizzling {
//In Objective-C you'd perform the swizzling in load(),
//but this method is not permitted in Swift
func initialize1()
{
let i: () -> () = {
let originalSelector = #selector(TestSwizzling.methodOne)
let swizzledSelector = #selector(TestSwizzling.methodTwo)
let originalMethod = class_getInstanceMethod(TestSwizzling.self, originalSelector);
let swizzledMethod = class_getInstanceMethod(TestSwizzling.self, swizzledSelector)
method_exchangeImplementations(originalMethod, swizzledMethod)
print("swizzled")
}
i()
}
func methodTwo()->Int{
// It will not be a recursive call anymore after the swizzling
return 4
}
}
var c = TestSwizzling()
c.initialize1()
print(c.methodOne())
print(c.methodTwo())
output: 4 1
输出:4 1