如何实现method swizzling swift 3.0?
How to implement method swizzling swift 3.0?
如何在 Swift 3.0 中实现方法调配?
我已阅读 nshipster article 相关内容,但在此代码块中
struct Static {
static var token: dispatch_once_t = 0
}
编译器报错
dispatch_once_t is unavailable in Swift: Use lazily initialized
globals instead
首先 dispatch_once_t
在 Swift 3.0 中不可用。
您可以从两个备选方案中进行选择:
全局变量
struct
、enum
或 class
的静态 属性
有关详细信息,请参阅
对于不同的目的,您必须使用不同的 swizzling 实现
- Swizzling CocoaTouch class, for example UIViewController;
- Swizzling custom Swift class;
混合 CocoaTouch class
使用全局变量
UIViewController
的 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 自定义 Swift class
要在您的 Swift classes 中使用 method swizzling,您必须满足两个要求 (for more details):
- 包含要调配的方法的 class 必须扩展
NSObject
- 您要调配的方法必须具有
dynamic
属性
以及自定义 Swift 基础 class Person
的示例调配方法
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)")
}
}
@TikhonovAlexander:很好的回答
我修改了 swizzler 以同时使用两个选择器并使其更通用。
Swift 4 / Swift 5
private let swizzling: (AnyClass, Selector, Selector) -> () = { forClass, originalSelector, swizzledSelector in
guard
let originalMethod = class_getInstanceMethod(forClass, originalSelector),
let swizzledMethod = class_getInstanceMethod(forClass, swizzledSelector)
else { return }
method_exchangeImplementations(originalMethod, swizzledMethod)
}
extension UIView {
static let classInit: Void = {
let originalSelector = #selector(layoutSubviews)
let swizzledSelector = #selector(swizzled_layoutSubviews)
swizzling(UIView.self, originalSelector, swizzledSelector)
}()
@objc func swizzled_layoutSubviews() {
swizzled_layoutSubviews()
print("swizzled_layoutSubviews")
}
}
// perform swizzling in AppDelegate.init()
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
override init() {
super.init()
UIView.classInit
}
}
Swift 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 5
import Foundation
class TestSwizzling : NSObject {
@objc dynamic func methodOne()->Int{
return 1
}
}
extension TestSwizzling {
@objc func methodTwo()->Int{
// It will not be a recursive call anymore after the swizzling
return 2
}
//In Objective-C you'd perform the swizzling in load(),
//but this method is not permitted in Swift
func swizzle(){
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()
}
}
var c = TestSwizzling()
print([c.methodOne(), c.methodTwo()]) // [1, 2]
c.swizzle() // swizzled!
print([c.methodOne(), c.methodTwo()]) // [2, 1]
Swift 5.1
Swift 使用 Objective-C 运行时特性使方法调配。给你两种方法。
注意:open override class func initialize() {}
不再允许。
class继承NSObject
,方法必须有dynamic
属性
class Father: NSObject {
@objc dynamic func makeMoney() {
print("make money")
}
}
extension Father {
static func swizzle() {
let originSelector = #selector(Father.makeMoney)
let swizzleSelector = #selector(Father.swizzle_makeMoney)
let originMethod = class_getInstanceMethod(Father.self, originSelector)
let swizzleMethod = class_getInstanceMethod(Father.self, swizzleSelector)
method_exchangeImplementations(originMethod!, swizzleMethod!)
}
@objc func swizzle_makeMoney() {
print("have a rest and make money")
}
}
Father.swizzle()
var tmp = Father()
tmp.makeMoney() // have a rest and make money
tmp.swizzle_makeMoney() // make money
- 使用
@_dynamicReplacement(for: )
class Father {
dynamic func makeMoney() {
print("make money")
}
}
extension Father {
@_dynamicReplacement(for: makeMoney())
func swizzle_makeMoney() {
print("have a rest and make money")
}
}
Father().makeMoney() // have a rest and make money
试试这个框架:https://github.com/623637646/SwiftHook
let object = MyObject()
let token = try? hookBefore(object: object, selector: #selector(MyObject.noArgsNoReturnFunc)) {
// run your code
print("hooked!")
}
object.noArgsNoReturnFunc()
token?.cancelHook() // cancel the hook
挂钩Swift中的方法非常容易。
Swift
调配
@objcMembers
class sA {
dynamic
class func sClassFooA() -> String {
return "class fooA"
}
dynamic
func sFooA() -> String {
return "fooA"
}
}
@objcMembers
class sB {
dynamic
class func sClassFooB() -> String {
return "class fooB"
}
dynamic
func sFooB() -> String {
return "fooB"
}
}
Swizzling.swift
import Foundation
@objcMembers
public class Swizzling: NSObject {
public class func sExchangeClass(cls1: AnyClass, sel1: Selector, cls2: AnyClass, sel2: Selector) {
let originalMethod = class_getClassMethod(cls1, sel1)
let swizzledMethod = class_getClassMethod(cls2, sel2)
method_exchangeImplementations(originalMethod!, swizzledMethod!)
}
public class func sExchangeInstance(cls1: AnyClass, sel1: Selector, cls2: AnyClass, sel2: Selector) {
let originalMethod = class_getInstanceMethod(cls1, sel1)
let swizzledMethod = class_getInstanceMethod(cls2, sel2)
method_exchangeImplementations(originalMethod!, swizzledMethod!)
}
}
通过Swift
使用
func testSExchangeClass() {
Swizzling.sExchangeClass(cls1: sA.self, sel1: #selector(sA.sClassFooA), cls2: sB.self, sel2: #selector(sB.sClassFooB))
XCTAssertEqual("class fooB", sA.sClassFooA())
}
func testSExchangeInstance() {
Swizzling.sExchangeInstance(cls1: sA.self, sel1: #selector(sA.sFooA), cls2: sB.self, sel2: #selector(sB.sFooB))
XCTAssertEqual("fooB", sA().sFooA())
}
[Add Objective-C as an consumer]
通过 Objective-C
使用
- (void)testSExchangeClass {
[Swizzling sExchangeClassWithCls1:[cA class] sel1:@selector(cClassFooA) cls2:[cB class] sel2:@selector(cClassFooB)];
XCTAssertEqual(@"class fooB", [cA cClassFooA]);
}
- (void)testSExchangeInstance {
[Swizzling sExchangeInstanceWithCls1:[cA class] sel1:@selector(cFooA) cls2:[cB class] sel2:@selector(cFooB)];
XCTAssertEqual(@"fooB", [[[cA alloc] init] cFooA]);
}
[Objective-C swizzling]
如何在 Swift 3.0 中实现方法调配?
我已阅读 nshipster article 相关内容,但在此代码块中
struct Static {
static var token: dispatch_once_t = 0
}
编译器报错
dispatch_once_t is unavailable in Swift: Use lazily initialized globals instead
首先 dispatch_once_t
在 Swift 3.0 中不可用。
您可以从两个备选方案中进行选择:
全局变量
struct
、enum
或class
的静态 属性
有关详细信息,请参阅
对于不同的目的,您必须使用不同的 swizzling 实现
- Swizzling CocoaTouch class, for example UIViewController;
- Swizzling custom Swift class;
混合 CocoaTouch class
使用全局变量
UIViewController
的 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 自定义 Swift class
要在您的 Swift classes 中使用 method swizzling,您必须满足两个要求 (for more details):
- 包含要调配的方法的 class 必须扩展
NSObject
- 您要调配的方法必须具有
dynamic
属性
以及自定义 Swift 基础 class Person
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)")
}
}
@TikhonovAlexander:很好的回答
我修改了 swizzler 以同时使用两个选择器并使其更通用。
Swift 4 / Swift 5
private let swizzling: (AnyClass, Selector, Selector) -> () = { forClass, originalSelector, swizzledSelector in
guard
let originalMethod = class_getInstanceMethod(forClass, originalSelector),
let swizzledMethod = class_getInstanceMethod(forClass, swizzledSelector)
else { return }
method_exchangeImplementations(originalMethod, swizzledMethod)
}
extension UIView {
static let classInit: Void = {
let originalSelector = #selector(layoutSubviews)
let swizzledSelector = #selector(swizzled_layoutSubviews)
swizzling(UIView.self, originalSelector, swizzledSelector)
}()
@objc func swizzled_layoutSubviews() {
swizzled_layoutSubviews()
print("swizzled_layoutSubviews")
}
}
// perform swizzling in AppDelegate.init()
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
override init() {
super.init()
UIView.classInit
}
}
Swift 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 5
import Foundation
class TestSwizzling : NSObject {
@objc dynamic func methodOne()->Int{
return 1
}
}
extension TestSwizzling {
@objc func methodTwo()->Int{
// It will not be a recursive call anymore after the swizzling
return 2
}
//In Objective-C you'd perform the swizzling in load(),
//but this method is not permitted in Swift
func swizzle(){
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()
}
}
var c = TestSwizzling()
print([c.methodOne(), c.methodTwo()]) // [1, 2]
c.swizzle() // swizzled!
print([c.methodOne(), c.methodTwo()]) // [2, 1]
Swift 5.1
Swift 使用 Objective-C 运行时特性使方法调配。给你两种方法。
注意:open override class func initialize() {}
不再允许。
class继承
NSObject
,方法必须有dynamic
属性class Father: NSObject { @objc dynamic func makeMoney() { print("make money") } } extension Father { static func swizzle() { let originSelector = #selector(Father.makeMoney) let swizzleSelector = #selector(Father.swizzle_makeMoney) let originMethod = class_getInstanceMethod(Father.self, originSelector) let swizzleMethod = class_getInstanceMethod(Father.self, swizzleSelector) method_exchangeImplementations(originMethod!, swizzleMethod!) } @objc func swizzle_makeMoney() { print("have a rest and make money") } } Father.swizzle() var tmp = Father() tmp.makeMoney() // have a rest and make money tmp.swizzle_makeMoney() // make money
- 使用
@_dynamicReplacement(for: )
class Father { dynamic func makeMoney() { print("make money") } } extension Father { @_dynamicReplacement(for: makeMoney()) func swizzle_makeMoney() { print("have a rest and make money") } } Father().makeMoney() // have a rest and make money
- 使用
试试这个框架:https://github.com/623637646/SwiftHook
let object = MyObject()
let token = try? hookBefore(object: object, selector: #selector(MyObject.noArgsNoReturnFunc)) {
// run your code
print("hooked!")
}
object.noArgsNoReturnFunc()
token?.cancelHook() // cancel the hook
挂钩Swift中的方法非常容易。
Swift
调配
@objcMembers
class sA {
dynamic
class func sClassFooA() -> String {
return "class fooA"
}
dynamic
func sFooA() -> String {
return "fooA"
}
}
@objcMembers
class sB {
dynamic
class func sClassFooB() -> String {
return "class fooB"
}
dynamic
func sFooB() -> String {
return "fooB"
}
}
Swizzling.swift
import Foundation
@objcMembers
public class Swizzling: NSObject {
public class func sExchangeClass(cls1: AnyClass, sel1: Selector, cls2: AnyClass, sel2: Selector) {
let originalMethod = class_getClassMethod(cls1, sel1)
let swizzledMethod = class_getClassMethod(cls2, sel2)
method_exchangeImplementations(originalMethod!, swizzledMethod!)
}
public class func sExchangeInstance(cls1: AnyClass, sel1: Selector, cls2: AnyClass, sel2: Selector) {
let originalMethod = class_getInstanceMethod(cls1, sel1)
let swizzledMethod = class_getInstanceMethod(cls2, sel2)
method_exchangeImplementations(originalMethod!, swizzledMethod!)
}
}
通过Swift
使用func testSExchangeClass() {
Swizzling.sExchangeClass(cls1: sA.self, sel1: #selector(sA.sClassFooA), cls2: sB.self, sel2: #selector(sB.sClassFooB))
XCTAssertEqual("class fooB", sA.sClassFooA())
}
func testSExchangeInstance() {
Swizzling.sExchangeInstance(cls1: sA.self, sel1: #selector(sA.sFooA), cls2: sB.self, sel2: #selector(sB.sFooB))
XCTAssertEqual("fooB", sA().sFooA())
}
[Add Objective-C as an consumer]
通过 Objective-C
使用- (void)testSExchangeClass {
[Swizzling sExchangeClassWithCls1:[cA class] sel1:@selector(cClassFooA) cls2:[cB class] sel2:@selector(cClassFooB)];
XCTAssertEqual(@"class fooB", [cA cClassFooA]);
}
- (void)testSExchangeInstance {
[Swizzling sExchangeInstanceWithCls1:[cA class] sel1:@selector(cFooA) cls2:[cB class] sel2:@selector(cFooB)];
XCTAssertEqual(@"fooB", [[[cA alloc] init] cFooA]);
}
[Objective-C swizzling]