如何在协议扩展中使用 #selector(myMethodName)?

9
protocol LazyUpdateable {
    func waitToDoStuff()
    func myMethodName()
}


extension LazyUpdateable where Self: NSObject {
    func waitToDoStuff() {
        self.performSelector(#selector(myMethodName), withObject: nil, afterDelay: 1.5)
    }

    func myMethodName() {

    }
}

通过这个更新,我遇到了错误Argument of #selector refers to a method that is not exposed to objective c,但是如果我使用旧的方法Selector("myMethodName"),我会得到一个警告,建议我采用更好的做法。在这种情况下是否可以使用#selector()呢?我已经尝试过在我的协议上设置@objc,但它不起作用。
以下是一个可以进行测试的 playground,它显示了在设置@objc后无法正常工作。
import Foundation
import UIKit
import XCPlayground


@objc protocol LazyUpdatable {
    optional func waitToDoStuff()
    optional func myMethodName()
}

extension LazyUpdatable where Self: UIViewController {
    func waitToDoStuff() {
        self.performSelector(#selector(myMethodName), withObject: nil, afterDelay: 1.5)
    }

    func myMethodName() {
        print("LOL")
    }
}


@objc
class AViewController: UIViewController, LazyUpdatable {
    func start() {
        waitToDoStuff()
    }
}

let aViewController = AViewController()
aViewController.start()

XCPlaygroundPage.currentPage.needsIndefiniteExecution = true
2个回答

3
使用 #selector() 或 Selector() 进行动态调度时,不会看到你的 Swift 协议扩展。如果可能的话,尽量避免使用 Objective-C。您可以使用 libdispatch 来实现相同的结果:
protocol LazyUpdatable {

    func waitToDoStuff()
    func myMethodName()
}

extension LazyUpdatable {

    func waitToDoStuff() {
        let dispatchTime = dispatch_time(DISPATCH_TIME_NOW, Int64(1.5 * Double(NSEC_PER_SEC)))
        dispatch_after(dispatchTime, dispatch_get_main_queue()) {
            self.myMethodName()
        }
    }

    func myMethodName() {
        print("Aloha!")
    }
}

class ViewController: UIViewController, LazyUpdatable {

    override func viewDidLoad() {
        super.viewDidLoad()
        waitToDoStuff()
    }
}

虽然这种方法不如使用选择器灵活,但它可以让您使用真正的Swift协议扩展。

编辑: 如果您想要取消调用方法,请尝试以下操作:

var lazyUpdatableCancelKey = UInt8(0)

protocol LazyUpdatable: class {

    func waitToDoStuff()
    func cancelDoingStuff()
    func myMethodName()
}

extension LazyUpdatable {

    func waitToDoStuff() {
        let dispatchTime = dispatch_time(DISPATCH_TIME_NOW, Int64(1.5 * Double(NSEC_PER_SEC)))
        dispatch_after(dispatchTime, dispatch_get_main_queue()) {
            if let shouldCancel = objc_getAssociatedObject(self, &lazyUpdatableCancelKey) as? Bool where shouldCancel == true {
                return
            }
            self.myMethodName()
        }
    }

    func cancelDoingStuff() {
        objc_setAssociatedObject(self, &lazyUpdatableCancelKey, true, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
    }

    func myMethodName() {
        print("Aloha!")
    }
}

class ViewController: UIViewController, LazyUpdatable {

    override func viewDidLoad() {
        super.viewDidLoad()
        waitToDoStuff()

        let dispatchTime = dispatch_time(DISPATCH_TIME_NOW, Int64(1.4 * Double(NSEC_PER_SEC)))
        dispatch_after(dispatchTime, dispatch_get_main_queue()) {
            self.cancelDoingStuff()
        }
    }
}

1
对于XCode 8.2.1和Swift 3,您可以像之前尝试的那样使用传统的Selector("methodName")。此外,您可以将其括在括号中Selector(("methodName"))以消除警告。
由于所有内容都包含在协议中,因此您很少会出现拼写错误导致崩溃的情况。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接