SwiftのrespondsToSelectorに相当するものは何ですか?


195

私はググってみましたが、相当するものを素早く見つけることができませんでしたrespondsToSelector:

これは私が見つけることができる唯一のものです(respondsToSelectorのSwift代替:)が、デリゲートの存在を確認するため、私の場合はあまり関連性がありません。新しいAPIが存在するかどうかを確認するだけのデリゲートはありません。デバイスで実行している場合、または以前のバージョンのAPIにフォールバックしない場合。


これらはすべて、オプションに置き換えられ、オプションチェーン
ジャック

Appleは、明示的に使用することをお勧めしますことを考えるNSClassFromStringと、respondsToSelector新しく実装された機能性をチェックするために他の仕組みの中で、私はどちらかのメカニズムが既に整備されている、またはリリース前にあるだろうと信じているようになってきました。Advanced Interop...WWDC のビデオをご覧ください。
David Berry

2
@Jack Wuしかし、新しいメソッドが、UIApplicationやUIViewControllerのような基本的な何かで導入された新しいものである場合はどうでしょうか。これらのオブジェクトはオプションではなく、新しいメソッドもオプションではありません。たとえば、UIApplcation:newVersionOfMethodXを呼び出す必要があるか、UIApplication:deprecatedVersionOfMethodXを呼び出す必要があるかをどのように確認できますか?(iOS 7のSwiftでアプリをビルドして実行できることを考えると、これは非常に一般的なシナリオになります)
Gruntcakes

あなたが心配している/心配していたAPIはApple APIですか?
GoZoner、2015年

@PotassiumPermanganate-もちろん、答えが「はい、私はApple APIを使用していた」場合、おそらく彼はif #available(...)Swift 2.xで使用respondsToSelectorして、そもそも使用を避けることができます。しかし、あなたはそれを知っていました。(apple.co/1SNGtMQ
GoZoner

回答:


170

前述のように、Swiftではほとんどの?場合、オプションの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に導入された新しいメソッドが存在するかどうかを確認しようとしていますが、今のところうまくいきません。上記に従って私が試みている方法は次のとおりです。let present = UIApplication.sharedApplication()。respondsToSelector(Selector( "redactedAsStillUnderNDA"))の場合 しかし、「条件付きバインディングのバインドされた値はオプションのタイプでなければなりません」というコンパイルエラーが発生します。
Gruntcakes 2014年

4
これらの行を分割する、そのlet x = 部分を削除する必要があります。基本的に、if let x = y構造はオプションの値をアンラップすることです(と同様!)。からBool戻ってきているためrespondsToSelector、コンパイラーは結果がオプションのタイプ(Bool?)ではないと不平を言っています。
エリック

var宣言されたsはどうなりますか?彼らはまだ同じように反応しますか?Objective-Cではif ( [obj respondsToSelector:@selector(setSomeVar:)] ) { ... }someVarプロパティに対して行うことができました。varSwiftのs でも同じように機能しますか?
AlejandroIván2015

オプションのビューコントローラーインスタンスがnilではないが、メソッドまたは関数がそのコントローラーに実装されていない場合はどうなりますか?オブジェクトがセレクターに応答するかどうかを確認する必要があると思います。
Ashish Pisey

私は「バリュータイプの『のUIViewController』にはメンバー『respondsToSelector』がありません」取得しています
のMichałZiobro

59

実際のSwiftの置き換えはありません。

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

someObject.someMethod?()

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

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
したがって、アプリは現在、実行されているOSのバージョンを明示的に認識している必要がありますか?iOS8にのみ存在するメソッドを呼び出したいのですが、存在しない場合は古いiOS7バージョンを使用します。新しいメソッドの有無をチェックする代わりに、iOS8かどうかを確認する必要がありますか?Swiftは良いですが、これは少しぎこちないようです。
Gruntcakes 2014年

@sulthan respondsToSelector:Appleの推奨事項として、使用すべきではないことを明確に示しているので、使用すべきではないと言っているところがわかりません。こちらをご覧ください
David Berry

@Davidはい、あなたrespondsToSelector:は実際に持っているのが良い数少ないケースの一つを見つけました。しかし、Swiftリファレンスを参照すると、さまざまなシステムバージョンのサブクラスの使用について説明されています。
スルタン2014年

それがOPの話ではなく、オプションのプロトコルの場合について話している場合は、オプションのチェーンを使用して明示的に許可します(指摘したとおり)。どちらにしても、「Appleが明示的に指示したことを行うのは避けるべきだ」と言うのは悪いアドバイスのようです。Appleは、実行時にオプション機能を決定および実行するためのメカニズムを「常に」提供しています。
David Berry

1
@Honey Swiftでは通常、代わりに可用性ディレクティブを使用しますif #available(iOS 10) {。たとえば、メソッドを直接呼び出します。
スルタン

40

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

4
if theMethod = delegate?.theOptionalProtocolMethod
john07

1
:複数の候補があるセレクタの構文の例let theMethod = delegate.userNotificationCenter(_:willPresent:withCompletionHandler:)
・クール

11

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

@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 { ... }

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

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

見る:

「オプションの連鎖によるメソッドの呼び出し」
抜粋:Apple Inc.「The Swift Programming Language」
iBooks。https://itun.es/us/jEUH0.l


これは、メソッドが何かを返す場合にのみ機能します。それがvoidメソッドの場合はどうなりますか?
Gruntcakes 2014年

1
voidが単純に使用する場合if object.method?(args) { ... }-存在する場合、メソッドの呼び出しは戻りVoidませんnil
GoZoner

1
ただし、その結果、「タイプボイドはプロトコル「論理値」に準拠していません」
Gruntcakes 2014年

参照ドキュメント(ページ311〜312)を参照してください。
GoZoner 2014年

「テストするメソッドが@objcプロトコルでオプションのメソッドとして定義されている場合」またはobjecttypeがある場合はAnyObject、任意の@objcメソッドをテストできます。
newacct 2014年

9

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

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

1
メソッドがオーバーロードされている場合はどうなりますか?特定のものを確認するにはどうすればよいですか?
NunoGonçalves15年


7

Swift 2で、Appleはと呼ばれる新機能を導入しましたAPI availability checking。これrespondsToSelector:はメソッドの代わりになる可能性があります。次のコードスニペット比較は、WWDC2015セッション106からコピーされたものです。もっと知る。

古いアプローチ:

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

respondsToSelectorSwift のアプローチを使用するよりもはるかに優れたソリューションだと思います。また、そもそもセレクターチェックがこの問題(可用性チェック)に対する最善の解決策ではなかったためでもあります。
JanApotheker 2017年

6

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

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çalves15年

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が存在し、それを実装する場合にのみ呼び出されます。


しかし、myQuestionalObjectがUIApplication(したがって存在するため、その存在をチェックしても意味がない)のようなものであり、importandMethod()がiOS Nで導入された新しいメソッドであり、それを呼び出すことができるか、 iOS N-1の古いdeprecatedImportantMethod()?
Gruntcakes 2014年

2
あなたはまた、必要な?importantMethod
newacct
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.