Apa yang setara dengan Swift dari respondsToSelector?

195

Saya sudah mencari di Google tetapi tidak dapat menemukan apa yang setara dengan swift respondsToSelector:.

Ini adalah satu-satunya hal yang dapat saya temukan ( alternatif Swift untuk respondToSelector:) tetapi tidak terlalu relevan dalam kasus saya karena memeriksa keberadaan delegasi, saya tidak memiliki delegasi yang saya hanya ingin memeriksa apakah ada API baru atau tidak ketika berjalan di perangkat dan jika tidak kembali ke versi api sebelumnya.

Gruntcakes
sumber
Semua ini dimaksudkan untuk diganti dengan Opsional, dan dilakukan dengan Chaining Opsional
Jack
Mengingat bahwa Apple secara eksplisit merekomendasikan penggunaan NSClassFromStringdan di respondsToSelectorantara mekanisme lainnya untuk memeriksa fungsionalitas yang baru diimplementasikan, saya harus percaya bahwa mekanisme sudah ada di tempat, atau akan ada sebelum rilis. Coba tonton Advanced Interop...videonya dari WWDC.
David Berry
2
@ Jack Wu Tapi bagaimana jika metode baru ini adalah sesuatu yang baru diperkenalkan pada sesuatu yang mendasar seperti UIApplication atau UIViewController. Objek-objek ini tidak opsional dan metode baru tidak opsional. Bagaimana Anda dapat memeriksa apakah Anda harus memanggil misalnya UIAplikasi: newVersionOfMethodX atau Anda harus memanggil UIAplikasi: deprecatedVersionOfMethodX? (Mengingat bahwa Anda dapat membangun dan menjalankan aplikasi di Swift di iOS 7 ini akan menjadi skenario yang sangat umum)
Gruntcakes
Apakah API Anda / Anda khawatir dengan Apple API?
GoZoner
@ PotassiumPermanganate - tentu saja, jika jawabannya adalah 'ya, saya menggunakan Apple API' maka mungkin dia bisa menggunakan if #available(...)Swift 2.x untuk menghindari penggunaan respondsToSelectordi tempat pertama. Tapi kamu tahu itu. ( apple.co/1SNGtMQ )
GoZoner

Jawaban:

170

Seperti yang disebutkan, di Swift sebagian besar waktu Anda dapat mencapai apa yang Anda butuhkan dengan ?operator pembuka bungkus opsional . Ini memungkinkan Anda untuk memanggil metode pada objek jika dan hanya jika objek itu ada (tidak nil) dan metode tersebut diterapkan.

Dalam kasus di mana Anda masih perlu respondsToSelector:, itu masih ada sebagai bagian dari NSObjectprotokol.

Jika Anda memanggil respondsToSelector:tipe Obj-C di Swift, maka itu berfungsi sama seperti yang Anda harapkan. Jika Anda menggunakannya di kelas Swift Anda sendiri, Anda harus memastikan kelas Anda berasal NSObject.

Berikut adalah contoh dari kelas Swift yang dapat Anda periksa jika merespons pemilih:

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

Penting agar Anda tidak mengabaikan nama parameter. Dalam contoh ini, Selector("sleep::")adalah tidak sama dengan Selector("sleep:minutes:").

Erik
sumber
Saya mencoba menentukan apakah ada metode baru yang diperkenalkan di iOS8 untuk aplikasi UIA dan sejauh ini saya tidak beruntung. Berikut adalah cara saya mencoba sesuai dengan di atas: jika biarkan sekarang = UIApplication.sharedApplication (). RespondsToSelector (Selector ("redactedAsStillUnderNDA")). Namun saya mendapatkan kesalahan kompilasi: "Nilai batas dalam ikatan bersyarat harus dari tipe opsional".
Gruntcakes
4
Anda harus memisahkan garis-garis itu atau menghapus let x = bagiannya. Pada dasarnya, if let x = ystruktur adalah untuk membuka nilai opsional (mirip dengan !). Karena Anda mendapatkan Boolkembali dari respondsToSelector, kompiler mengeluh bahwa hasilnya bukan tipe opsional ( Bool?).
Erik
Apa yang akan terjadi dengan varyang dinyatakan? Apakah mereka masih menanggapi dengan cara yang sama? Di Objective-C saya bisa lakukan if ( [obj respondsToSelector:@selector(setSomeVar:)] ) { ... }untuk someVarproperti. Apakah itu bekerja dengan cara yang sama dengan vars di Swift?
Alejandro Iván
Bagaimana jika Lihat kontroler contoh opsional bukan nol, tetapi metode atau fungsi tidak diimplementasikan di controller itu? Saya kira Anda perlu memeriksa apakah objek merespons pemilih atau tidak.
Ashish Pisey
Saya mendapatkan "Nilai tipe 'UIViewController' tidak memiliki anggota 'respondsToSelector'"
Michał Ziobro
59

Tidak ada pengganti Swift nyata.

Anda dapat memeriksa dengan cara berikut:

someObject.someMethod?()

Ini memanggil metode someMethodhanya jika itu didefinisikan pada objek someObjecttetapi Anda dapat menggunakannya hanya untuk @objcprotokol yang menyatakan metode sebagai optional.

Swift secara inheren adalah bahasa yang aman sehingga setiap kali Anda memanggil metode, Swift harus tahu metode itu ada di sana. Tidak ada pemeriksaan runtime yang memungkinkan. Anda tidak bisa hanya memanggil metode acak pada objek acak.

Bahkan di Obj-C Anda harus menghindari hal-hal seperti itu jika memungkinkan karena tidak cocok dengan ARC (ARC kemudian memicu peringatan untuk performSelector:).

Namun, saat memeriksa API yang tersedia, Anda masih dapat menggunakan respondsToSelector:, bahkan jika Swift, jika Anda berurusan dengan NSObjectinstance:

@interface TestA : NSObject

- (void)someMethod;

@end

@implementation TestA

//this triggers a warning

@end   


var a = TestA()

if a.respondsToSelector("someMethod") {
   a.someMethod()
}
Sulthan
sumber
1
Jadi karena itu aplikasi sekarang harus tahu secara eksplisit versi OS apa yang mereka jalankan? Saya ingin memanggil metode yang hanya ada di iOS8 dan jika tidak ada, gunakan versi iOS7 yang lama. Jadi alih-alih memeriksa ada / tidaknya metode baru, saya malah harus mencari tahu apakah saya menggunakan iOS8 atau tidak? Swift bagus tapi ini agak kikuk.
Gruntcakes
@sulthan Saya tidak yakin dari mana Anda berasal menyatakan bahwa Anda tidak boleh menggunakan respondsToSelector:karena rekomendasi Apple secara eksplisit bahwa Anda harus melakukannya. Lihat di sini
David Berry
@ David Ya, Anda telah menemukan salah satu dari beberapa kasus respondsToSelector:yang sebenarnya bagus untuk dimiliki. Tetapi jika Anda membaca referensi Swift, mereka berbicara tentang menggunakan subclass untuk versi sistem yang berbeda.
Sulthan
Jika itu bukan kasus yang dibicarakan OP, dan mereka membicarakan tentang kasus protokol opsional, maka mereka juga secara eksplisit mengizinkannya menggunakan rantai tambahan (seperti yang telah Anda tunjukkan). Either way, mengatakan "Anda harus menghindari melakukan apa yang Apple telah secara eksplisit mengatakan kepada Anda untuk melakukan" sepertinya nasihat yang buruk. Apple "selalu" menyediakan mekanisme untuk menentukan dan menggunakan fungsionalitas opsional pada saat run-time.
David Berry
1
@ Madu Di Swift umumnya kita menggunakan arahan ketersediaan, misalnya if #available(iOS 10) {dan kemudian memanggil metode secara langsung.
Sulthan
40

Pembaruan 20 Mar 2017 untuk sintaks Swift 3:

Jika Anda tidak peduli apakah ada metode opsional, panggil saja delegate?.optionalMethod?()

Kalau tidak, menggunakan guardmungkin merupakan pendekatan terbaik:

weak var delegate: SomeDelegateWithOptionals?

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

Jawaban asli:

Anda dapat menggunakan pendekatan "jika dibiarkan" untuk menguji protokol opsional seperti ini:

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()
}
hyouuu
sumber
4
jika biarkan theMethod = delegate? .theOptionalProtocolMethod
john07
1
Contoh sintaks pemilih ketika ada beberapa kandidat:let theMethod = delegate.userNotificationCenter(_:willPresent:withCompletionHandler:)
Cœur
11

Tampaknya Anda perlu mendefinisikan protokol Anda sebagai subprotocol dari NSObjectProtocol ... maka Anda akan mendapatkan metode respondsToSelector

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

perhatikan bahwa hanya menetapkan @objc tidak cukup. Anda juga harus berhati-hati bahwa delegasi yang sebenarnya adalah subkelas dari NSObject - yang mungkin tidak ada di Swift.

Matej Ukmar
sumber
10

Jika metode yang Anda uji didefinisikan sebagai metode opsional dalam protokol @objc (yang terdengar seperti kasus Anda), maka gunakan pola rangkaian opsional sebagai:

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

Ketika metode ini dinyatakan sebagai kembali Void, cukup gunakan:

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

Lihat:

“Metode Memanggil Melalui Rantai Pilihan”
Kutipan Dari: Apple Inc. “Bahasa Pemrograman Swift.”
iBooks. https://itun.es/us/jEUH0.l

GoZoner
sumber
Ini hanya akan berfungsi jika metode mengembalikan sesuatu, bagaimana jika itu adalah metode kosong?
Gruntcakes
1
Jika batal menggunakan hanya if object.method?(args) { ... }- panggilan metode, bila ada, akan kembali Voidyang tidaknil
GoZoner
1
Namun yang menghasilkan "Tipe Void tidak sesuai dengan protokol" Nilai Logika ""
Gruntcakes
Silakan lihat dokumentasi yang dirujuk (halaman 311-312).
GoZoner
"Jika metode yang Anda uji didefinisikan sebagai metode opsional dalam protokol @objc" Atau jika objectmemiliki tipe AnyObject, Anda dapat menguji metode @objc apa pun.
Newacct
9

Fungsi adalah tipe kelas satu di Swift, jadi Anda dapat memeriksa apakah fungsi opsional yang didefinisikan dalam protokol telah diterapkan dengan membandingkannya dengan nihil:

if (someObject.someMethod != nil) {
    someObject.someMethod!(someArgument)
} else {
    // do something else
}
Christopher Pickslay
sumber
1
Bagaimana jika metode ini kelebihan beban? Bagaimana kita memeriksa yang spesifik?
Nuno Gonçalves
8

Untuk swift3

Jika Anda hanya ingin memanggil metode, jalankan kode di bawah ini.

self.delegate?.method?()

Jason Yu
sumber
7

Di Swift 2, Apple memperkenalkan fitur baru yang disebut API availability checking, yang mungkin menjadi pengganti metode respondsToSelector:. Perbandingan cuplikan kode berikut disalin dari WWDC2015 Sesi 106 Apa yang Baru di Swift yang saya pikir mungkin dapat membantu Anda, silakan periksa jika Anda perlu tahu lebih banyak.

Pendekatan Lama:

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

Pendekatan yang Lebih Baik:

@IBOutlet var dropButton: NSButton!
override func awakeFromNib() {
    if #available(OSX 10.10.3, *) {
        dropButton.springLoaded = true
    }
}
tounaobun
sumber
Saya pikir itu solusi yang jauh lebih baik daripada menggunakan respondsToSelectorpendekatan di Swift. Itu juga karena pemeriksaan pemilih bukan solusi terbaik untuk masalah ini (pengecekan ketersediaan) sejak awal.
JanApotheker
6

Untuk 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)")
    }
}
Kakashi
sumber
4

Saat ini (Swift 2.1) Anda dapat memeriksanya menggunakan 3 cara:

  1. Menggunakan respondsToSelector dijawab oleh @Erik_at_Digit
  2. Menggunakan '?' dijawab oleh @Sulthan

  3. Dan menggunakan as?operator:

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

Pada dasarnya itu tergantung pada apa yang ingin Anda capai:

  • Jika misalnya logika aplikasi Anda perlu melakukan beberapa tindakan dan delegasi tidak disetel atau delegasi yang ditunjuk tidak mengimplementasikan metode onSuccess () (metode protokol) sehingga opsi 1 dan 3 adalah pilihan terbaik, meskipun saya akan menggunakan opsi 3 yang merupakan cara Swift.
  • Jika Anda tidak ingin melakukan apa pun ketika delegasi nihil atau metode tidak diterapkan maka gunakan opsi 2.
OhadM
sumber
2

Saya hanya mengimplementasikan ini sendiri dalam sebuah proyek, lihat kode di bawah ini. Seperti yang disebutkan oleh @Christopher Pickslay, penting untuk diingat bahwa fungsi adalah warga negara kelas satu dan karenanya dapat diperlakukan seperti variabel opsional.

@objc protocol ContactDetailsDelegate: class {

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

...

weak var delegate:ContactDetailsDelegate!

if let deleteContact = delegate.deleteContact {
    deleteContact(contact)
}
Bulwinkel
sumber
Bagaimana jika metode ini kelebihan beban? Bagaimana caranya?
Nuno Gonçalves
2

sintaks lain yang mungkin oleh swift ..

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

Ketika saya mulai memperbarui proyek lama saya ke Swift 3.2, saya hanya perlu mengubah metode dari

respondsToSelector(selector)

untuk:

responds(to: selector)
chlexlex
sumber
0

Saya menggunakan guard let else, sehingga dapat melakukan beberapa hal standar jika fungsi delegasi tidak diimplementasikan.

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

Swift 3:

protokol

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

Obyek

class SomeObject : NSObject {

weak var delegate:SomeObject?

func delegateMethod() {

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

   }

}
etzuk
sumber
-4

Setara dengan itu? operator:

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

importantMethod hanya akan dipanggil jika myQuestionableObject ada dan mengimplementasikannya.

Ben Gottlieb
sumber
Tetapi bagaimana jika myQuestionalObject adalah sesuatu seperti aplikasi UIA (yang karenanya akan ada dan dengan demikian memeriksa keberadaannya tidak ada artinya) dan importandMethod () adalah metode baru yang diperkenalkan di iOS N dan Anda ingin menentukan apakah Anda dapat memanggilnya atau harus memanggil old deprecatedImportantMethod () di iOS N-1?
Gruntcakes
2
Anda juga memerlukan ?afterimportantMethod
newacct