Tương đương Swift của responsSToSelector là gì?


195

Tôi đã googled nhưng không thể tìm ra những gì nhanh chóng tương đương respondsToSelector:là.

Đây là điều duy nhất tôi có thể tìm thấy ( Swift thay thế để trả lời ToSelector :) nhưng không quá liên quan trong trường hợp của tôi vì nó kiểm tra sự tồn tại của đại biểu, tôi không có đại biểu Tôi chỉ muốn kiểm tra xem API mới có tồn tại không hoặc không khi chạy trên thiết bị và nếu không rơi trở lại phiên bản api trước đó.


Tất cả những thứ này đều được thay thế bằng Tùy chọn và được thực hiện với Chuỗi tùy chọn
Jack

Do Apple rõ ràng khuyên bạn nên sử dụng NSClassFromStringrespondsToSelectortrong số các cơ chế khác để kiểm tra chức năng mới được triển khai, tôi đã tin rằng các cơ chế đã được áp dụng hoặc sẽ có trước khi phát hành. Hãy thử xem Advanced Interop...video từ WWDC.
David Berry

2
@Jack Wu Nhưng nếu phương thức mới là thứ gì đó mới được giới thiệu trên một cái gì đó cơ bản như UIApplication hoặc UIViewControll. Các đối tượng này không phải là tùy chọn và phương thức mới không phải là tùy chọn. Làm thế nào bạn có thể kiểm tra nếu bạn phải gọi ví dụ UIApplcation: newVersionOfMethodX hoặc bạn phải gọi UIApplication: deprecatedVersionOfMethodX? (Cho rằng bạn có thể xây dựng và chạy một ứng dụng trong Swift trên iOS 7, đây sẽ là một tình huống rất phổ biến)
Gruntcakes

API bạn đang / quan tâm với API của Apple phải không?
GoZoner

@PotaliPermanganate - tất nhiên, nếu câu trả lời là 'có, tôi đã sử dụng API của Apple' thì có lẽ anh ta có thể sử dụng if #available(...)trong Swift 2.x để tránh sử dụng respondsToSelectorngay từ đầu. Nhưng bạn biết điều đó. ( apple.co/1SNGtMQ )
GoZoner

Câu trả lời:


170

Như đã đề cập, trong Swift hầu hết thời gian bạn có thể đạt được những gì bạn cần với ?toán tử mở khóa tùy chọn . Điều này cho phép bạn gọi một phương thức trên một đối tượng nếu và chỉ khi đối tượng tồn tại (không nil) và phương thức được thực hiện.

Trong trường hợp bạn vẫn cần respondsToSelector:, nó vẫn ở đó như một phần của NSObjectgiao thức.

Nếu bạn đang gọi respondsToSelector:loại Obj-C trong Swift, thì nó hoạt động giống như bạn mong đợi. Nếu bạn đang sử dụng nó trên lớp Swift của riêng bạn, bạn sẽ cần đảm bảo rằng lớp của bạn xuất phát từ đó NSObject.

Đây là một ví dụ về lớp Swift mà bạn có thể kiểm tra xem nó có phản hồi với bộ chọn không:

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

Điều quan trọng là bạn không bỏ qua các tên tham số. Trong ví dụ này, Selector("sleep::")không giống như Selector("sleep:minutes:").


Tôi đang cố gắng xác định xem một phương pháp mới được giới thiệu trong iOS8 cho UIApplication có hay không và cho đến nay không gặp may mắn. Đây là cách tôi đang cố gắng theo như trên: if let Present = UIApplication. SharedApplication (). ResponsesToSelector (Selector ("redactedAsStillUnderNDA")). Tuy nhiên, tôi nhận được một lỗi biên dịch: "Giá trị ràng buộc trong một ràng buộc có điều kiện phải là loại tùy chọn".
Gruntcakes

4
Bạn sẽ cần phải tách những dòng đó hoặc loại bỏ let x = phần. Về cơ bản, if let x = ycấu trúc là hủy bỏ các giá trị tùy chọn (tương tự !). Vì bạn đang lấy Boollại từ respondsToSelector, trình biên dịch phàn nàn rằng kết quả không phải là một loại tùy chọn ( Bool?).
Erik

Điều gì sẽ xảy ra với vars tuyên bố? Họ vẫn trả lời theo cách tương tự? Trong Mục tiêu-C tôi có thể làm if ( [obj respondsToSelector:@selector(setSomeVar:)] ) { ... }cho một someVartài sản. Nó có hoạt động tương tự với vars trong Swift không?
Alejandro Iván

Điều gì xảy ra nếu tùy chọn View của trình điều khiển không phải là con số không, nhưng phương thức hoặc hàm không được triển khai trong bộ điều khiển đó? Tôi đoán bạn sẽ cần kiểm tra xem đối tượng có phản hồi với bộ chọn hay không.
Ashish Pisey

Tôi đang nhận được "Giá trị của loại 'UIViewControll' không có thành viên '
responssToSelector

59

Không có thay thế Swift thực sự.

Bạn có thể kiểm tra theo cách sau:

someObject.someMethod?()

Điều này chỉ gọi phương thức someMethodnếu nó được định nghĩa trên đối tượng someObjectnhưng bạn chỉ có thể sử dụng nó cho các @objcgiao thức đã khai báo phương thức là optional.

Swift vốn dĩ là một ngôn ngữ an toàn nên mỗi khi bạn gọi một phương thức Swift phải biết phương thức đó là có. Không có kiểm tra thời gian chạy là có thể. Bạn không thể chỉ gọi các phương thức ngẫu nhiên trên các đối tượng ngẫu nhiên.

Ngay cả trong Obj-C, bạn cũng nên tránh những thứ như vậy khi có thể vì nó không chơi tốt với ARC (ARC sau đó kích hoạt cảnh báo cho performSelector:).

Tuy nhiên, khi kiểm tra các API có sẵn, bạn vẫn có thể sử dụng respondsToSelector:, ngay cả khi Swift, nếu bạn đang xử lý các NSObjecttrường hợp:

@interface TestA : NSObject

- (void)someMethod;

@end

@implementation TestA

//this triggers a warning

@end   


var a = TestA()

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

1
Vì vậy, các ứng dụng bây giờ phải biết rõ phiên bản HĐH nào chúng đang chạy? Tôi muốn gọi một phương thức chỉ tồn tại trong iOS8 và nếu nó không sử dụng phiên bản iOS7 cũ. Vì vậy, thay vì kiểm tra sự hiện diện / vắng mặt của phương pháp mới, thay vào đó tôi phải tìm hiểu xem tôi có trên iOS8 hay không? Swift là tốt nhưng điều này có vẻ hơi clunky.
Gruntcakes

@sulthan Tôi không chắc chắn bạn đến từ đâu mà nói rằng bạn không nên sử dụng respondsToSelector:vì khuyến nghị của Apple rõ ràng là bạn nên làm điều đó. Xem tại đây
David Berry

@David Vâng, bạn đã tìm thấy một trong số ít trường hợp respondsToSelector:thực sự tốt để có. Nhưng nếu bạn xem qua tham chiếu Swift, họ sẽ nói về việc sử dụng các lớp con cho các phiên bản hệ thống khác nhau.
Sulthan

Nếu đó không phải là trường hợp mà OP đang nói đến và họ đang nói về trường hợp giao thức tùy chọn, thì họ cũng cho phép rõ ràng cho phép sử dụng chuỗi tùy chọn (như bạn đã chỉ ra). Dù bằng cách nào, nói rằng "bạn nên tránh làm những gì Apple đã nói rõ ràng với bạn để làm" có vẻ như là lời khuyên tồi. Apple đã "luôn luôn" cung cấp các cơ chế để xác định và thực hiện chức năng tùy chọn trong thời gian chạy.
David Berry

1
@Honey Trong Swift, chúng tôi thường sử dụng các chỉ thị sẵn có thay vào đó, ví dụ if #available(iOS 10) {và sau đó gọi phương thức trực tiếp.
Sulthan

40

Cập nhật ngày 20 tháng 3 năm 2017 cho cú pháp Swift 3:

Nếu bạn không quan tâm liệu phương thức tùy chọn có tồn tại hay không, chỉ cần gọi delegate?.optionalMethod?()

Mặt khác, sử dụng guardcó lẽ là cách tiếp cận tốt nhất:

weak var delegate: SomeDelegateWithOptionals?

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

Câu trả lời gốc:

Bạn có thể sử dụng phương pháp "if let" để kiểm tra một giao thức tùy chọn như thế này:

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 let theMethod = ủy nhiệm? .theOptionalProtocolMethod
john07

1
Ví dụ về cú pháp selector khi có nhiều ứng cử viên:let theMethod = delegate.userNotificationCenter(_:willPresent:withCompletionHandler:)
Cœur

11

Có vẻ như bạn cần xác định giao thức của mình là giao thức con của NSObjectProtocol ... sau đó bạn sẽ nhận được phương thức phản hồi ToSelector

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

lưu ý rằng chỉ xác định @objc là không đủ. Bạn cũng nên cẩn thận rằng đại biểu thực tế là một lớp con của NSObject - điều mà trong Swift có thể không có.


10

Nếu phương thức bạn đang kiểm tra được xác định là phương thức tùy chọn trong giao thức @objc (có vẻ giống trường hợp của bạn), thì hãy sử dụng mẫu chuỗi tùy chọn như:

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

Khi phương thức được khai báo là trả về Void, chỉ cần sử dụng:

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

Xem:

Các phương thức gọi điện thoại thông qua việc xâu chuỗi tùy chọn
Trích dẫn từ: Apple Inc. Ngôn ngữ lập trình Swift.
Sách điện tử. https://itun.es/us/jEUH0.l


Điều này sẽ chỉ hoạt động nếu phương thức trả về một cái gì đó, nếu nó là một phương thức void thì sao?
Gruntcakes

1
Nếu void sử dụng đơn giản if object.method?(args) { ... }- cuộc gọi của phương thức, khi nó tồn tại, sẽ trả về Voidkhông phải lànil
GoZoner

1
Tuy nhiên, kết quả là "Loại Void không tuân thủ giao thức" Giá trị logic ""
Gruntcakes

Vui lòng xem tài liệu tham khảo (trang 311-312).
GoZoner

"Nếu phương thức bạn đang kiểm tra được xác định là phương thức tùy chọn trong giao thức @objc" Hoặc nếu objectcó loại AnyObject, bạn có thể kiểm tra bất kỳ phương thức @objc nào.
newacct

9

Các hàm là các loại hạng nhất trong Swift, vì vậy bạn có thể kiểm tra xem một hàm tùy chọn được xác định trong giao thức có được thực hiện hay không bằng cách so sánh nó với nil:

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

1
Nếu phương thức bị quá tải thì sao? Làm thế nào chúng ta có thể kiểm tra cho một cụ thể?
Nuno Gonçalves

8

Dành cho swift3

Nếu bạn chỉ muốn gọi phương thức, hãy chạy mã bên dưới.

self.delegate?.method?()


7

Trong Swift 2, Apple đã giới thiệu một tính năng mới được gọi là API availability checkingcó thể thay thế cho respondsToSelector:phương thức . So sánh đoạn mã sau đây được sao chép từ Phiên bản WWDC2015 106 Có gì mới trong Swift mà tôi nghĩ có thể giúp bạn, vui lòng kiểm tra nếu bạn cần biêt nhiêu hơn.

Phương pháp cũ:

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

Cách tiếp cận tốt hơn:

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

Tôi nghĩ đó là một giải pháp tốt hơn nhiều so với sử dụng respondsToSelectorphương pháp tiếp cận trong Swift. Điều đó cũng bởi vì kiểm tra bộ chọn không phải là giải pháp tốt nhất cho vấn đề này (kiểm tra tính khả dụng) ngay từ đầu.
JanApotheker

6

Đối với 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

Hiện tại (Swift 2.1) bạn có thể kiểm tra bằng 3 cách:

  1. Sử dụng respondsToSelector trả lời bằng cách @Erik_at_Digit
  2. Sử dụng '?' được trả lời bởi @Sulthan

  3. Và sử dụng as?toán tử:

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

Về cơ bản, nó phụ thuộc vào những gì bạn đang cố gắng để đạt được:

  • Ví dụ, nếu logic ứng dụng của bạn cần thực hiện một số hành động và đại biểu không được đặt hoặc đại biểu nhọn không thực hiện phương thức onSuccess () (phương thức giao thức) thì tùy chọn 1 và 3 là lựa chọn tốt nhất, mặc dù tôi sử dụng tùy chọn 3 đó là cách Swift.
  • Nếu bạn không muốn làm bất cứ điều gì khi ủy nhiệm không hoặc phương thức không được triển khai thì hãy sử dụng tùy chọn 2.

2

Tôi chỉ thực hiện điều này trong một dự án, xem mã dưới đây. Như @Christopher Pickslay đã đề cập, điều quan trọng cần nhớ là các hàm là công dân hạng nhất và do đó có thể được coi như các biến tùy chọn.

@objc protocol ContactDetailsDelegate: class {

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

...

weak var delegate:ContactDetailsDelegate!

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

Nếu phương thức bị quá tải thì sao? Làm thế nào một người sẽ làm điều đó?
Nuno Gonçalves

2

một cú pháp khác có thể bằng swift ..

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

2

Khi tôi bắt đầu cập nhật dự án cũ của mình lên Swift 3.2, tôi chỉ cần thay đổi phương thức từ

respondsToSelector(selector)

đến:

responds(to: selector)

0

Tôi sử dụng guard let else, để có thể thực hiện một số công cụ mặc định nếu func của đại biểu không được triển khai.

@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

Swift 3:

giao thức

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

Vật

class SomeObject : NSObject {

weak var delegate:SomeObject?

func delegateMethod() {

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

   }

}

-4

Tương đương là? nhà điều hành:

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

Quan trọng sẽ chỉ được gọi nếu myQuestionableObject tồn tại và thực hiện nó.


Nhưng điều gì xảy ra nếu myQuestionalObject giống như UIApplication (do đó sẽ tồn tại và do đó kiểm tra sự tồn tại của nó là vô nghĩa) và importandMethod () là một phương thức mới được giới thiệu trong iOS N và bạn muốn xác định xem bạn có thể gọi nó hay phải gọi nó không cũ deprecatedImportantMethod () trên iOS N-1?
Gruntcakes

2
bạn cũng cần ?sauimportantMethod
newacct
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.