【问题标题】:How to use #selector(myMethodName) in a protocol extension?如何在协议扩展中使用#selector(myMethodName)?
【发布时间】:2016-03-23 09:55:26
【问题描述】:
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,我已经尝试过了。

这是一个您可以尝试的游乐场,它表明它不适用于设置 @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

【问题讨论】:

    标签: swift2 protocols


    【解决方案1】:

    使用 #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()
            }
        }
    }
    

    【讨论】:

      【解决方案2】:

      适用于 XCode 8.2.1 和 Swift 3。

      您可以像尝试过的那样使用传统的Selector("methodName")。此外,您可以将警告用括号 Selector(("methodName")) 括起来来消除警告

      由于一切都在您的协议范围内,因此您不太可能出现最终崩溃的拼写错误

      【讨论】:

        猜你喜欢
        • 2016-07-11
        • 1970-01-01
        • 2021-03-29
        • 1970-01-01
        • 1970-01-01
        • 2012-08-02
        • 2018-09-04
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多