【问题标题】:What is the Swift equivalent of respondsToSelector?respondsToSelector 的 Swift 等价物是什么?
【发布时间】:2014-08-01 19:09:02
【问题描述】:

我在谷歌上搜索过,但无法找出与 respondsToSelector: 等效的 swift 是什么。

这是我唯一能找到的 (Swift alternative to respondsToSelector:),但与我的情况不太相关,因为它检查委托的存在,我没有委托我只想检查是否有新的 API在设备上运行时是否存在,如果不回退到以前版本的 api。

【问题讨论】:

  • 所有这些都被替换为 Optional,并通过 Optional Chaining 来执行
  • 鉴于 Apple 明确建议使用 NSClassFromStringrespondsToSelector 以及其他机制来检查新实现的功能,我必须相信这些机制要么已经到位,要么将在那里发布前。尝试观看来自 WWDC 的 Advanced Interop... 视频。
  • @Jack Wu 但是,如果新方法是在诸如 UIApplication 或 UIViewController 之类的基础上引入的新方法呢?这些对象不是可选的,新方法也不是可选的。如何检查是否必须调用例如 UIApplcation:newVersionOfMethodX 或必须调用 UIApplication:deprecatedVersionOfMethodX? (鉴于您可以在 iOS 7 上使用 Swift 构建和运行应用程序,这将是一个非常常见的场景)
  • 您关注/曾经关注的 API 是否与 Apple API 相关?
  • @PotassiumPermanganate - 当然,如果答案是“是的,我使用的是 Apple API”,那么也许他可以在 Swift 2.x 中使用 if #available(...) 以避免首先使用 respondsToSelector .但你知道的。 (apple.co/1SNGtMQ)

标签: objective-c swift selector


【解决方案1】:

如前所述,在 Swift 中,大多数情况下,您可以使用 ? 可选的解包操作符来实现您所需要的。当且仅当对象存在(不是nil)并且方法已实现时,这允许您调用对象上的方法。

如果您仍然需要respondsToSelector:,它仍然是NSObject 协议的一部分。

如果您在 Swift 中对 Obj-C 类型调用 respondsToSelector:,那么它的工作方式与您预期的相同。如果您在自己的 Swift 类上使用它,则需要确保您的类派生自 NSObject

这是一个 Swift 类的示例,您可以检查它是否响应选择器:

class Worker : NSObject
{
    func work() { }
    func eat(food: AnyObject) { }
    func sleep(hours: Int, minutes: Int) { }
}

let worker = Worker()

let canWork = worker.respondsToSelector(Selector("work"))   // true
let canEat = worker.respondsToSelector(Selector("eat:"))    // true
let canSleep = worker.respondsToSelector(Selector("sleep:minutes:"))    // true
let canQuit = worker.respondsToSelector(Selector("quit"))   // false

不要遗漏参数名称,这一点很重要。在此示例中,Selector("sleep::")Selector("sleep:minutes:") 相同。

【讨论】:

  • 我正在尝试确定是否存在在 iOS8 中引入 UIApplication 的新方法,但到目前为止我没有运气。这是我根据上述尝试的方法:if let present = UIApplication.sharedApplication().respondsToSelector(Selector("redactedAsStillUnderNDA"))。但是我得到一个编译错误:“条件绑定中的绑定值必须是可选类型”。
  • 您需要拆分这些行删除let x = 部分。基本上,if let x = y 结构是展开可选值(类似于!)。由于您从respondsToSelector 得到了Bool,编译器抱怨结果不是可选类型(Bool?)。
  • 声明的vars 会发生什么?他们仍然以同样的方式回应吗?在 Objective-C 中,我可以为 someVar 属性执行 if ( [obj respondsToSelector:@selector(setSomeVar:)] ) { ... }。它与 Swift 中的 vars 的工作方式相同吗?
  • 如果 View 控制器实例 optional 不是 nil,但该控制器中没有实现方法或功能怎么办?我猜你需要检查对象是否响应选择器。
  • 我收到“'UIViewController' 类型的值没有成员 'respondsToSelector'”
【解决方案2】:

没有真正的 Swift 替代品。

您可以通过以下方式查看:

someObject.someMethod?()

这仅在对象someObject 上定义了方法someMethod 时才调用它,但您只能将它用于已将方法声明为optional@objc 协议。

Swift 本质上是一种安全的语言,因此每次调用方法时,Swift 都必须知道该方法是否存在。无法进行运行时检查。您不能只对随机对象调用随机方法。

即使在 Obj-C 中,您也应该尽可能避免此类事情,因为它不能很好地与 ARC 配合使用(ARC 会触发 performSelector: 的警告)。

但是,在检查可用 API 时,您仍然可以使用 respondsToSelector:,即使是 Swift,如果您正在处理 NSObject 实例:

@interface TestA : NSObject

- (void)someMethod;

@end

@implementation TestA

//this triggers a warning

@end   


var a = TestA()

if a.respondsToSelector("someMethod") {
   a.someMethod()
}

【讨论】:

  • 因此,应用程序现在必须明确知道它们运行的​​操作系统版本是什么?我想调用一个只存在于 iOS8 中的方法,如果它不存在,则使用旧的 iOS7 版本。因此,我不必检查新方法的存在/不存在,而是必须找出我是否在 iOS8 上? Swift 很好,但这似乎有点笨拙。
  • @sulthan 我不确定你从哪里来,说你不应该使用respondsToSelector:,因为 Apple 明确建议你应该这样做。见here
  • @David 是的,您已经找到了respondsToSelector: 实际上很好的少数情况之一。但是如果你仔细阅读 Swift 参考资料,他们会谈到为不同的系统版本使用子类。
  • 如果 OP 不是在谈论这种情况,并且他们在谈论可选协议情况,那么他们也明确允许使用可选链接(正如您所指出的那样)。不管怎样,说“你应该避免做苹果明确告诉你做的事情”似乎是个坏建议。 Apple“始终”提供了在运行时确定和执行可选功能的机制。
  • @Honey 在 Swift 中,我们通常使用可用性指令,例如if #available(iOS 10) { 然后直接调用方法。
【解决方案3】:

2017 年 3 月 20 日更新 Swift 3 语法:

如果不关心可选方法是否存在,直接调用delegate?.optionalMethod?()

否则,使用guard 可能是最好的方法:

weak var delegate: SomeDelegateWithOptionals?

func someMethod() {
    guard let method = delegate?.optionalMethod else {
        // optional not implemented
        alternativeMethod()
        return
    }
    method()
}

原答案:

您可以使用“if let”方法来测试这样的可选协议:

weak var delegate: SomeDelegateWithOptionals?

func someMethod() {
  if let delegate = delegate {
    if let theMethod = delegate.theOptionalProtocolMethod? {
      theMethod()
      return
    }
  }
  // Reaching here means the delegate doesn't exist or doesn't respond to the optional method
  alternativeMethod()
}

【讨论】:

  • 如果让 theMethod = delegate?.theOptionalProtocolMethod
  • 存在多个候选时的选择器语法示例:let theMethod = delegate.userNotificationCenter(_:willPresent:withCompletionHandler:)
  • 只是另一个澄清的例子:let willSelectRowAt = delegate?.tableView(_:willSelectRowAt:)
【解决方案4】:

如果您要测试的方法在 @objc 协议中定义为 可选方法(听起来像您的情况),则使用 可选链接 模式为:

if let result = object.method?(args) {
  /* method exists, result assigned, use result */
}
else { ... }

当方法声明为返回Void时,只需使用:

if object.method?(args) { ... }

见:

“通过可选链调用方法”
摘自:Apple Inc. “Swift 编程语言”。
电子书。 https://itun.es/us/jEUH0.l

【讨论】:

  • 这只有在方法返回一些东西时才有效,如果它是一个 void 方法怎么办?
  • 如果 void 仅使用 if object.method?(args) { ... } - 方法的调用,当它存在时,将返回 Void 不是 nil
  • 但是导致“Type Void 不符合协议“逻辑值””
  • 请参阅参考文档(第 311-312 页)。
  • "如果您要测试的方法被定义为@objc 协议中的可选方法" 或者如果object 的类型为AnyObject,您可以测试任何@objc 方法。
【解决方案5】:

看来您需要将协议定义为 NSObjectProtocol 的子协议...然后您将获得 respondsToSelector 方法

@objc protocol YourDelegate : NSObjectProtocol
{
    func yourDelegateMethod(passObject: SomeObject)
}

请注意,仅指定 @objc 是不够的。您还应该注意实际的委托是 NSObject 的子类 - 在 Swift 中可能不是。

【讨论】:

    【解决方案6】:

    函数是 Swift 中的一等类型,因此您可以通过将其与 nil 进行比较来检查协议中定义的可选函数是否已实现:

    if (someObject.someMethod != nil) {
        someObject.someMethod!(someArgument)
    } else {
        // do something else
    }
    

    【讨论】:

    • 方法重载了怎么办?我们如何检查特定的?
    【解决方案7】:

    对于 swift3

    如果你只是想调用方法,运行下面的代码。

    self.delegate?.method?()

    【讨论】:

      【解决方案8】:

      在 Swift 2 中,Apple 引入了一个名为 API availability checking 的新功能,它可能会替代 respondsToSelector: 方法。以下代码 sn-p 比较是从 WWDC2015 Session 106 What's New in Swift 复制的,我认为这可能会有所帮助您,如果您需要了解更多信息,请查看。

      旧方法:

      @IBOutlet var dropButton: NSButton!
      override func awakeFromNib() {
          if dropButton.respondsToSelector("setSpringLoaded:") {
              dropButton.springLoaded = true
          }
      }
      

      更好的方法:

      @IBOutlet var dropButton: NSButton!
      override func awakeFromNib() {
          if #available(OSX 10.10.3, *) {
              dropButton.springLoaded = true
          }
      }
      

      【讨论】:

      • 我认为这比在 Swift 中使用 respondsToSelector 方法要好得多。这也是因为选择器检查并不是解决这个问题(可用性检查)的最佳方法。
      【解决方案9】:

      对于 swift 3.0

      import UIKit
      
      @objc protocol ADelegate : NSObjectProtocol {
      
          @objc optional func hi1()
          @objc optional func hi2(message1:String, message2:String)
      }
      
      class SomeObject : NSObject {
      
          weak var delegate:ADelegate?
      
          func run() {
      
              // single method
              if let methodHi1 = delegate?.hi1 {
                  methodHi1()
              } else {
                  print("fail h1")
              }
      
              // multiple parameters
              if let methodHi2 = delegate?.hi2 {
                  methodHi2("superman", "batman")
              } else {
                  print("fail h2")
              }
          }
      }
      
      class ViewController: UIViewController, ADelegate {
      
          let someObject = SomeObject()
      
          override func viewDidLoad() {
              super.viewDidLoad()
      
              someObject.delegate = self
              someObject.run()
          }
      
          // MARK: ADelegate
          func hi1() {
      
              print("Hi")
          }
      
          func hi2(message1: String, message2: String) {
      
              print("Hi \(message1) \(message2)")
          }
      }
      

      【讨论】:

        【解决方案10】:

        目前(Swift 2.1)您可以使用 3 种方式检查它:

        1. 使用@Erik_at_Digit 回答的respondsToSelector
        2. 使用@Sulthan 回答的'?'

        3. 并使用as? 运算符:

          if let delegateMe = self.delegate as? YourCustomViewController
          {
             delegateMe.onSuccess()
          }
          

        基本上这取决于您要达到的目标:

        • 例如,如果您的应用程序逻辑需要执行某些操作并且委托未设置或指定委托未实现 onSuccess() 方法(协议方法),则选项 1 和 3 是最佳选择,尽管我'd 使用选项 3,这是 Swift 方式。
        • 如果您不想在委托为 nil 或方法未实现时执行任何操作,请使用选项 2。

        【讨论】:

          【解决方案11】:

          当我开始将旧项目更新到 Swift 3.2 时,我只需要将方法从

          respondsToSelector(selector)
          

          到:

          responds(to: selector)
          

          【讨论】:

            【解决方案12】:

            我只是自己在一个项目中实现这个,见下面的代码。正如@Christopher Pickslay 提到的,重要的是要记住函数是一等公民,因此可以被视为可选变量。

            @objc protocol ContactDetailsDelegate: class {
            
                optional func deleteContact(contact: Contact) -> NSError?
            }
            
            ...
            
            weak var delegate:ContactDetailsDelegate!
            
            if let deleteContact = delegate.deleteContact {
                deleteContact(contact)
            }
            

            【讨论】:

            • 方法重载了怎么办?如何做到这一点?
            【解决方案13】:

            swift 的另一种可能的语法..

             if let delegate = self.delegate, method = delegate.somemethod{
                    method()
                }
            

            【讨论】:

              【解决方案14】:

              我使用guard let else,所以如果没有实现委托函数,它可以做一些默认的东西。

              @objc protocol ViewController2Delegate: NSObjectProtocol {
              
                  optional func viewController2(controller: ViewController2, didSomethingWithStringAndReturnVoid string: String)
              
                  optional func viewController2(controller: ViewController2, didSomethingWithStringAndReturnString string: String) -> String
              }
              
              class ViewController2: UIViewController {
              
                  weak var delegate: ViewController2Delegate?        
              
                  @IBAction func onVoidButtonClicked(sender: AnyObject){
              
                      if (delegate != nil && delegate!.respondsToSelector(Selector("viewController2:didSomethingWithStringAndReturnVoid:"))) {
                          NSLog("ReturnVoid is implemented")
              
                          delegate!.viewController2!(self, didSomethingWithStringAndReturnVoid: "dummy")
                      }
                      else{
                          NSLog("ReturnVoid is not implemented")
                          // Do something by default
                      }
                  }
              
                  @IBAction func onStringButtonClicked(sender: AnyObject){
              
                      guard let result = delegate?.viewController2?(self, didSomethingWithStringAndReturnString: "dummy") else {
                          NSLog("ReturnString is not implemented")
                          // Do something by default
                          return
                      }
              
                      NSLog("ReturnString is implemented with result: \(result)")
                  }
              }
              

              【讨论】:

                【解决方案15】:

                我猜你想为委托做一个默认实现。你可以这样做:

                let defaultHandler = {}
                (delegate?.method ?? defaultHandler)()
                

                【讨论】:

                  【解决方案16】:

                  斯威夫特 3:

                  协议

                  @objc protocol SomeDelegate {
                      @objc optional func method()
                  }
                  

                  对象

                  class SomeObject : NSObject {
                  
                  weak var delegate:SomeObject?
                  
                  func delegateMethod() {
                  
                       if let delegateMethod = delegate?.method{
                           delegateMethod()
                       }else {
                          //Failed
                       }
                  
                     }
                  
                  }
                  

                  【讨论】:

                    【解决方案17】:

                    等价于 ?运营商:

                    var value: NSNumber? = myQuestionableObject?.importantMethod()
                    

                    importantMethod 只有在 myQuestionableObject 存在并实现它时才会被调用。

                    【讨论】:

                    • 但是如果 myQuestionalObject 类似于 UIApplication (因此会存在,因此检查它的存在是没有意义的)并且 importandMethod() 是 iOS N 中引入的一种新方法,并且您想确定您是否可以调用它还是必须在 iOS N-1 上调用旧的 deprecatedImportantMethod()?
                    • importantMethod之后你还需要一个?
                    猜你喜欢
                    • 2014-08-12
                    • 1970-01-01
                    • 1970-01-01
                    • 2014-09-24
                    • 2016-12-13
                    • 2014-07-31
                    • 1970-01-01
                    相关资源
                    最近更新 更多