Swift等效于reactsToSelector是什么?


195

我已经用谷歌搜索,但无法找出什么是等效respondsToSelector:的。

这是我唯一能找到的东西(Swifts替代responsToSelector:),但在我的案例中并不太相关,因为它检查委托的存在,我没有委托,我只想检查是否存在新的API在设备上运行时是否返回,如果不是,则返回原先的api版本。


所有这些都意味着与选配更换,并行使与可选的链接
杰克

鉴于Apple明确建议使用NSClassFromStringrespondsToSelector其他机制来检查新实现的功能,因此我必须相信这些机制已经存在,或者将在发布之前存在。尝试观看Advanced Interop...来自WWDC 的视频。
David Berry

2
@Jack Wu但是,如果新方法是在UIApplication或UIViewController之类的基础上引入的新方法,该怎么办。这些对象不是可选的,新方法也不是可选的。如何检查是否必须调用例如UIApplcation:newVersionOfMethodX或必须调用UIApplication:deprecatedVersionOfMethodX?(鉴于您可以在iOS 7上的Swift中构建和运行应用程序,这将是非常常见的情况)
Gruntcakes 2014年

您是否正在使用与Apple API相关的API?
GoZoner 2015年

@PotassiumPermanganate-当然,如果答案是“是的,我正在使用Apple API”,那么也许他可以if #available(...)在Swift 2.x中使用respondsToSelector,以避免首先使用。但是你知道的。(apple.co/1SNGtMQ
GoZoner

Answers:


170

如前所述,在大多数情况下,您可以使用?可选的unwrapper运算符来实现所需的功能。这样,当且仅当对象存在(不存在nil)并且实现了该方法时,您才可以在该对象上调用方法。

在您仍然需要的情况下respondsToSelector:,它仍然是NSObject协议的一部分。

如果您respondsToSelector:在Swift 中调用Obj-C类型,则其工作原理与您期望的相同。如果要在自己的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的新方法,到目前为止还算不上什么。这是我根据上面的方法尝试的方法:如果让我们= UIApplication.sharedApplication()。respondsToSelector(Selector(“ redactedAsStillUnderNDA”))。但是,我收到一个编译错误:“条件绑定中的绑定值必须是可选类型”。
Gruntcakes

4
您需要拆分这些行删除该let x = 部分。基本上,if let x = y结构是要包装可选值(类似于!)。由于您Bool从中获得了回报respondsToSelector,因此编译器抱怨结果不是可选类型(Bool?)。
Erik 2014年

var声明会发生什么?他们仍然以同样的方式回应吗?在Objective-C中,我可以做if ( [obj respondsToSelector:@selector(setSomeVar:)] ) { ... }一个someVar财产。var在Swift中,它与s 是否以相同的方式工作?
亚历杭德罗·伊万2015年

如果View控制器实例的可选值不是nil,但该控制器中未实现方法或函数怎么办?我猜您将需要检查对象是否响应选择器。
Ashish Pisey '16

我得到“类型的值‘的UIViewController’没有成员‘respondsToSelector’”
米哈尔Ziobro

59

没有真正的Swift替代品。

您可以通过以下方式进行检查:

someObject.someMethod?()

someMethod仅在对象上定义了该方法时,才调用该方法,someObject但是您只能将其@objc声明为的协议使用该方法optional

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

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

但是,在检查可用的API时respondsToSelector:,即使正在处理NSObject实例,即使使用Swift 也可以使用:

@interface TestA : NSObject

- (void)someMethod;

@end

@implementation TestA

//this triggers a warning

@end   


var a = TestA()

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

1
因此,应用程序现在必须明确知道它们运行在哪个版本的操作系统上?我想调用仅在iOS8中存在的方法,如果不存在,请使用旧的iOS7版本。因此,代替检查是否存在新方法,我不得不找出我是否在iOS8上?Swift很好,但这似乎有些笨拙。
Gruntcakes 2014年

@sulthan我不确定您表示不应该使用respondsToSelector:Apple的建议是因为您明确地应该这样做。看到这里
David Berry 2014年

@David是的,您发现了respondsToSelector:实际上很不错的少数情况之一。但是,如果您遍历Swift参考资料,他们会谈到将子类用于不同的系统版本。
Sulthan 2014年

如果不是OP在谈论这种情况,而他们在谈论可选协议的情况,那么他们还明确地允许使用可选链接(如您所指出的)。不管怎么说,说“你应该避免做苹果明确告诉你要做的事情”似乎是个坏建议。苹果公司“总是”提供了在运行时确定和执行可选功能的机制。
David Berry

1
@Honey在Swift中,我们通常改用可用性指令,例如if #available(iOS 10) {,然后直接调用该方法。
苏珊(Sulthan)'18年

40

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()
}

4
要是让theMethod =委托.theOptionalProtocolMethod?
john07

1
选择器语法的实施例,当有多个候选:let theMethod = delegate.userNotificationCenter(_:willPresent:withCompletionHandler:)
心教堂

11

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

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

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


10

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

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

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

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

看到:

“呼叫通过可选的链接方法”
摘自:苹果公司“雨燕编程语言。”
iBooks。https://itun.es/us/jEUH0.l


仅当该方法返回某些内容时,此方法才有效;如果它是void方法,该怎么办?
Gruntcakes

1
如果简单地使用无效if object.method?(args) { ... }-该方法的调用,当它存在,将返回Void这是不是nil
GoZoner

1
但是,这导致“无效类型不符合协议“逻辑值””
Gruntcakes 2014年

请参阅参考文档(311-312页)。
GoZoner 2014年

“如果要测试的方法在@objc协议中定义为可选方法,或者object具有type AnyObject,则可以测试任何@objc方法。
newacct

9

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

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

1
如果该方法重载了怎么办?我们如何检查特定的?
NunoGonçalves2015年

8

迅捷3

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

self.delegate?.method?()


7

在Swift 2中,Apple引入了一个名为的新功能API availability checking,它可能是respondsToSelector:方法的替代。下面的代码片段比较摘自WWDC2015 Session 106 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
    }
}

我认为这是比respondsToSelector在Swift中使用方法更好的解决方案。这也是因为选择器检查并不是解决此问题(可用性检查)的最佳方法。
JanApotheker

6

快速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)")
    }
}

4

当前(Swift 2.1),您可以使用3种方式进行检查:

  1. 使用respondsToSelector回答@Erik_at_Digit
  2. 使用“?” @Sulthan回答

  3. 并使用as?运算符:

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

基本上,这取决于您要实现的目标:

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

2

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

@objc protocol ContactDetailsDelegate: class {

    optional func deleteContact(contact: Contact) -> NSError?
}

...

weak var delegate:ContactDetailsDelegate!

if let deleteContact = delegate.deleteContact {
    deleteContact(contact)
}

如果该方法重载了怎么办?那怎么办?
NunoGonçalves2015年

2

快速的另一种可能的语法。

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

2

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

respondsToSelector(selector)

至:

responds(to: selector)

0

我使用guard let else,因此如果未实现委托func,则可以做一些默认的事情。

@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)")
    }
}

-1

斯威夫特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
     }

   }

}

-4

相当于是?操作员:

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

仅当myQuestionableObject存在并实现它时,才会调用ImportantMethod。


但是,如果myQuestionalObject是UIApplication之类的东西(因此将存在并且因此检查其存在是没有意义的),而importandMethod()是iOS N中引入的新方法,您想确定是否可以调用它或必须调用iOS N-1上的旧deprecatedImportantMethod()吗?
Gruntcakes

2
你还需要一个?importantMethod
newacct
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.