ios - ¿Cómo implementar el método swizzling swift 3.0?
xcode swift3 (3)
¿Cómo puedo implementar el método swizzling en Swift 3.0 ?
He leído el artículo de nshipster al respecto, pero en el fragmento de este código
struct Static {
static var token: dispatch_once_t = 0
}
el compilador me da un error
dispatch_once_t no está disponible en Swift: en su lugar, use globales globalizados perezosamente
@TikhonovAlexander: Gran respuesta
Modifiqué el swizzler para tomar ambos selectores y lo hice más genérico.
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 4
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
}
}
En primer lugar,
dispatch_once_t
no está disponible en Swift 3.0.
Puedes elegir entre dos alternativas:
-
Variable global
-
Propiedad estática de
struct
,enum
oclass
Para más detalles, vea que Whither dispatch_once en Swift 3
Para diferentes propósitos debes usar diferentes implementaciones de swizzling
- Clase Swizzling CocoaTouch, por ejemplo UIViewController;
- Swift clase Swift personalizada;
Clase Swatching CocoaTouch
ejemplo swizzling
viewWillAppear(_:)
de
UIViewController
usando variable global
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)")
}
}
Clase Swift personalizada Swizzling
Para utilizar el método swizzling con sus clases Swift, debe cumplir dos requisitos ( para obtener más detalles ):
-
La clase que contiene los métodos que se deben mezclar debe extender
NSObject
-
Los métodos que desea mezclar deben tener el atributo
dynamic
Y ejemplo, método swizzling de la clase base personalizada Swift
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)")
}
}
chapoteando en el patio de recreo
Swift 4.2
import Foundation
class TestSwizzling : NSObject {
@objc 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 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()
}
@objc func methodTwo()->Int{
// It will not be a recursive call anymore after the swizzling
return 4
}
}
var c = TestSwizzling()
print([c.methodOne(),c.methodTwo()])
c.swizzle()
print([c.methodOne(),c.methodTwo()])
salida:
[1, 4]
swizzled
[4, 1]