如何实现swift swift 3.0的方法?

时间:2016-09-18 21:32:00

标签: ios swift xcode swift3 swizzling

如何在 Swift 3.0 中实现方法调整?

我已经阅读了nshipster article,但是在此代码的块中

struct Static {
    static var token: dispatch_once_t = 0
}

编译器给我一个错误

  

dispatch_once_t在Swift中不可用:使用懒惰初始化   而是全局变量

6 个答案:

答案 0 :(得分:56)

首先,dispatch_once_t在Swift 3.0中不可用。 您可以从两种选择中进行选择:

  1. 全局变量

  2. structenumclass的静态属性

  3. 有关详细信息,请参阅Whither dispatch_once in Swift 3

    出于不同的目的,您必须使用不同的混合实现

      
        
    • Swizzling CocoaTouch类,例如UIViewController;
    •   
    • Swizzling自定义Swift类;
    •   

    Swizzling CocoaTouch类

    示例使用全局变量调整viewWillAppear(_:) UIViewController

    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类

    要使用Swift类调整方法,必须遵守两个要求(for more details):

    • 包含要调整的方法的类必须扩展NSObject
    • 您想要调配的方法必须具有dynamic属性

    自定义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)")
        }
    }
    

答案 1 :(得分:27)

@TikhonovAlexander:很棒的答案

我修改了swizzler以获取两个选择器并使其更通用。

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
    }

}

答案 2 :(得分:3)

在操场上晃悠着 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()])

输出:
[1,4]
绞合
[4,1]

答案 3 :(得分:1)

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]

答案 4 :(得分:0)

尝试以下框架:https://github.com/623637646/SwiftHook

const mongoose = require('mongoose');
mongoose.connect('mongodb://localhost/reviews', { useNewUrlParser: true, useUnifiedTopology: true, useCreateIndex: true });
const Schema = mongoose.Schema;
const productSchema = new Schema({
    product_id: { type: Number, required: true, unique: true },
    product_name: { type: String, required: true, unique: true },
    review: [{
        review_id: { type: Number, required: true, unique: true },
        user: {
            user_id: { type: Number },
            firstname: { type: String },
            lastname: { type: String },
            gender: { type: String, enum: ['Male', 'Female', 'Other'] },
            nickname: { type: String },
            email: { type: String, required: true },
            password: { type: String, required: true },
        },
        opinion: { type: String, required: true },
        text: { type: String },
        rating_overall: { type: Number, min: 1, max: 5, required: true },
        doesRecommended: { type: Boolean, required: true },
        rating_size: { type: String, enum: ['a size too small', '1/2 a size too small', 'Perfect', '1/2 a size too big', 'a size too big'], required: true },
        rating_width: { type: String, enum: ['Too narrow', 'Slightly narrow', 'Perfect', 'Slightly wide', 'Too wide'], required: true },
        rating_comfort: { type: String, enum: ['Uncomfortable', 'Slightly uncomfortable', 'Ok', 'Comfortable', 'Perfect'], required: true },
        rating_quality: { type: String, enum: ['Poor', 'Below average', 'What I expected', 'Pretty great', 'Perfect'], required: true },
        isHelpful: { type: Number, required: true, default: 0 },
        isNotHelpful: { type: Number, required: true, default: 0 },
        created_at: { type: Date, required: true },
        review_photo_path: [{
            review_photo_id: { type: Number },
            review_photo_url: { type: String }
        }]
    }]
});
const ProductModel = mongoose.model('product', productSchema);
module.exports = { ProductModel };

在Swift中挂接方法非常容易。

答案 5 :(得分:-1)

Swift 5.1

快速使用Objective-C运行时功能使方法混乱。这是两种方式。

注意:requests不再被允许。

  1. 类继承open override class func initialize() {},并且方法必须具有NSObject属性

    dynamic
    1. 使用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: )