web-dev-qa-db-ja.com

SwiftのrespondsToSelectorとは何ですか?

私はグーグルしましたが、SwiftのrespondsToSelector:に相当するものが何かを見つけることができませんでした。

これが私が見つけることができる唯一のものです( respondsToSelector: に代わるものとして)デバイス上で実行しているときに新しいAPIが存在するかどうかを確認し、存在しない場合は以前のバージョンのAPIにフォールバックします。

184
Gruntcakes

前述のように、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::")notSelector("sleep:minutes:")と同じです。

159
Erik

本当のSwiftの置き換えはありません。

次の方法で確認できます。

someObject.someMethod?()

これは、メソッドsomeMethodがオブジェクトsomeObjectで定義されている場合にのみ呼び出されますが、メソッドをoptionalとして宣言している@objcプロトコルに対してのみ使用できます。

Swiftは本質的に安全な言語なので、Swiftはメソッドを呼び出すたびにそのメソッドがあることを知っている必要があります。実行時検査はできません。ランダムオブジェクトに対してランダムメソッドを呼び出すだけではいけません。

Obj-CでもARCではうまく動作しないので、可能であればこのようなことは避けるべきです(ARCはperformSelector:に対して警告を発します)。

しかしながら、利用可能なAPIをチェックするとき、もしあなたがNSObjectインスタンスを扱っていれば、SwiftであってもrespondsToSelector:を使うことができます:

@interface TestA : NSObject

- (void)someMethod;

@end

@implementation TestA

//this triggers a warning

@end   


var a = TestA()

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

Swift 3シンタックスの2017年3月20日更新:

オプションのメソッドが存在するかどうか気にしないのであれば、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()
}
34
hyouuu

プロトコルをNSObjectProtocolのサブプロトコルとして定義する必要があるようです... respondsToSelectorメソッドを取得します。

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

@objcを指定するだけでは不十分であることに注意してください。実際のデリゲートがNSObjectのサブクラスであることにも注意する必要があります - Swiftではそうではないかもしれません。

11
Matej Ukmar

テストしているメソッドが@ objcプロトコルでオプションのメソッドとして定義されている場合(あなたの場合のように聞こえます)、次に、オプションの連鎖パターンを次のように使用します。

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

メソッドがVoidを返すように宣言されている場合は、単に次のように使用します。

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

見る:

「オプションチェーンによるメソッド呼び出し」
抜粋:アップル社 "The Swift Programming Language"。
iBooks https://itun.es/us/jEUH0.l

10
GoZoner

関数はSwiftのファーストクラス型なので、プロトコルで定義されているオプションの関数がnilと比較することで実装されているかどうかを確認できます。

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

Swift 2では、AppleがAPI availability checkingという新しい機能を導入しました。これは、respondsToSelector:メソッドの代わりになる可能性があります。以下のコードスニペットの比較は、WWDC2015のセッションからコピーされたものです。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
    }
}
7
tounaobun

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)")
    }
}
6
Kakashi

Swift3用

あなただけのメソッドを呼び出したい場合は、以下のコードを実行します。

self.delegate?.method?()

5
Jason Yu

現在(Swift 2.1)、3つの方法で確認できます。

  1. を使用して respondsToSelector @Erik_at_Digitが回答しました
  2. を使用して '?' @スルタンで回答

  3. そしてas?演算子を使う:

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

基本的にそれはあなたが達成しようとしていることによります:

  • たとえば、アプリケーションロジックで何らかのアクションを実行する必要があり、デリゲートが設定されていない、または指示されたデリゲートがonSuccess()メソッド(プロトコルメソッド)を実装していない場合迅速な方法であるオプション3。
  • デリゲートがnilであるかメソッドが実装されていないときに何もしたくない場合は、オプション2を使用してください。
4
OhadM

swiftによる他の可能な構文.

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

私はこれを自分でプロジェクトに実装するだけです。下記のコードを参照してください。 @Christopher Pickslayが述べているように、関数はファーストクラスの市民であり、したがってオプション変数のように扱うことができることを覚えておくことが重要です。

@objc protocol ContactDetailsDelegate: class {

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

...

weak var delegate:ContactDetailsDelegate!

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

古いプロジェクトをSwift 3.2にアップデートし始めたので、メソッドを次のように変更する必要がありました。

respondsToSelector(selector)

に:

responds(to: selector)
1
chAlexey

私は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)")
    }
}
0
dichen