问题描述:

I've googled but not been able to find out what the swift equivalent to respondsToSelector: is.

This is the only thing I could find (Swift alternative to respondsToSelector:) but isn't too relevant in my case as its checking the existence of the delegate, I don't have a delegate I just want to check if a new API exists or not when running on the device and if not fall back to a previous version of the api.

网友答案:

As mentioned, in Swift most of the time you can achieve what you need with the ? optional unwrapper operator. This allows you to call a method on an object if and only if the object exists (not nil) and the method is implemented.

In the case where you still need respondsToSelector:, it is still there as part of the NSObject protocol.

If you are calling respondsToSelector: on an Obj-C type in Swift, then it works the same as you would expect. If you are using it on your own Swift class, you will need to ensure your class derives from NSObject.

Here's an example of a Swift class that you can check if it responds to a selector:

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

It is important that you do not leave out the parameter names. In this example, Selector("sleep::") is not the same as Selector("sleep:minutes:").

网友答案:

There is no real Swift replacement.

You can check in the following way:

someObject.someMethod?()

This calls the method someMethod only if it's defined on object someObject but you can use it only for @objc protocols which have declared the method as optional.

Swift is inherently a safe language so everytime you call a method Swift has to know the method is there. No runtime checking is possible. You can't just call random methods on random objects.

Even in Obj-C you should avoid such things when possible because it doesn't play well with ARC (ARC then triggers warnings for performSelector:).

However, when checking for available APIs, you can still use respondsToSelector:, even if Swift, if you are dealing with NSObject instances:

@interface TestA : NSObject

- (void)someMethod;

@end

@implementation TestA

//this triggers a warning

@end   


var a = TestA()

if a.respondsToSelector("someMethod") {
   a.someMethod()
}
网友答案:

You can use the "if let" approach to test an optional protocol like this:

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()
}
网友答案:

It seems you need to define your protocol as as subprotocol of NSObjectProtocol ... then you'll get respondsToSelector method

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

note that only specifying @objc was not enough. You should be also careful that the actual delegate is a subclass of NSObject - which in Swift might not be.

网友答案:

If the method you are testing for is defined as an optional method in a @objc protocol (which sounds like your case), then use the optional chaining pattern as:

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

When the method is declare as returning Void, simply use:

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

See:

“Calling Methods Through Optional Chaining”
Excerpt From: Apple Inc. “The Swift Programming Language.”
iBooks. https://itun.es/us/jEUH0.l

网友答案:

In Swift 2,Apple introduced a new feature called API availability checking, which might be a replacement for respondsToSelector: method.The following code snippet comparison is copied from the WWDC2015 Session 106 What's New in Swift which I thought might help you,please check it out if you need to know more.

The Old Approach:

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

The Better Approach:

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

Functions are first-class types in Swift, so you can check whether an optional function defined in a protocol has been implemented by comparing it to nil:

if (someObject.someMethod != nil) {
    someObject.someMethod!(someArgument)
} else {
    // do something else
}
网友答案:

Currently (Swift 2.1) you can check it using 3 ways:

  1. Using respondsToSelector answered by @Erik_at_Digit
  2. Using '?' answered by @Sulthan

  3. And using as? operator:

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

Basically it depends on what you are trying to achieve:

  • If for example your app logic need to perform some action and the delegate isn't set or the pointed delegate didn't implement the onSuccess() method (protocol method) so option 1 and 3 are the best choice, though I'd use option 3 which is Swift way.
  • If you don't want to do anything when delegate is nil or method isn't implemented then use option 2.
网友答案:

For 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 delegate != nil && delegate!.responds(to: #selector(ADelegate.hi1)) {

            delegate?.hi1!()
        }
        else {
            print("fail h1")
        }

        // multiple parameters
        if delegate != nil && delegate!.responds(to: #selector(ADelegate.hi2(message1:message2:))) {

            delegate?.hi2!(message1: "superman", message2: "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)")
    }
}
网友答案:

I just implement this myself in a project, see code below. As mentions by @Christopher Pickslay it is important to remember that functions are first class citizens and can therefore be treated like optional variables.

@objc protocol ContactDetailsDelegate: class {

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

...

weak var delegate:ContactDetailsDelegate!

if let deleteContact = delegate.deleteContact {
    deleteContact(contact)
}
网友答案:

another possible syntax by swift..

 if let delegate = self.delegate, method = delegate.somemethod{
        method()
    }
网友答案:

It seems that this issues is mainly about protocol's optional method.

@objc protocol MyClassDelegate {
    optional func optionalFunc() -> Int
}

weak var deleagate: MyClassDelegate?

// Call the method without Runtime Error 
// if delegate was nil or optionalFunc was note implemented.
delegate?.optionalFunc?()

// Call the method and get a returned value without Runetime Error.
if let value = delegate?.optionalFunc?() {
   // Do something.
}

// Just check the method is implemented.
if delegate?.optionalFunc != nil {
   // Do something.
}
网友答案:

I use guard let else, so that can do some default stuff if the delegate func is not implemented.

@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)")
    }
}
网友答案:

Swift 3:

protocol

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

Object

class SomeObject : NSObject {

weak var delegate:SomeObject?

func delegateMethod() {

     if let delegateMethod = delegate?.method{
         delegateMethod()
     }else {
        //Failed
     }

   }

}
网友答案:

The equivalent is the ? operator:

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

importantMethod will only be called if myQuestionableObject exists and implements it.

相关阅读:
Top