อะไรคือ Swift ที่เทียบเท่ากับ responssToSelector?


195

ฉัน googled แต่ไม่สามารถค้นหาสิ่งที่เทียบเท่ากับที่respondsToSelector:รวดเร็ว

นี่เป็นสิ่งเดียวที่ฉันสามารถหาได้ ( ทางเลือกที่รวดเร็วในการตอบสนองต่อ SelectSelector:)แต่ไม่เกี่ยวข้องกันมากเกินไปในกรณีที่ตรวจสอบการมีอยู่ของผู้รับมอบสิทธิ์ฉันไม่มีผู้รับมอบสิทธิ์ฉันแค่ต้องการตรวจสอบว่ามี API ใหม่หรือไม่ หรือไม่เมื่อทำงานบนอุปกรณ์และหากไม่ถอยกลับไปเป็นรุ่นก่อนหน้าของ API


สิ่งเหล่านี้ทั้งหมดจะถูกแทนที่ด้วย Optionals และออกกำลังกายด้วยการผูกมัดตัวเลือก
Jack

เนื่องจาก Apple แนะนำอย่างชัดเจนให้ใช้NSClassFromStringและrespondsToSelectorในกลไกอื่น ๆ เพื่อตรวจสอบการทำงานที่เพิ่งนำมาใช้ใหม่ฉันต้องเชื่อว่ากลไกนั้นมีอยู่แล้วหรือจะอยู่ที่นั่นก่อนจะปล่อย ลองรับชมAdvanced Interop...วิดีโอจาก WWDC
David Berry

2
@ Jack Wu แต่ถ้าวิธีการใหม่เป็นสิ่งใหม่ที่แนะนำในสิ่งพื้นฐานเช่น UIApplication หรือ UIViewController วัตถุเหล่านี้ไม่ใช่ทางเลือกและวิธีการใหม่นั้นไม่จำเป็น คุณจะตรวจสอบได้อย่างไรว่าคุณต้องโทรหาตัวอย่าง UIApplcation: newVersionOfMethodX หรือคุณต้องเรียกใช้ UIApplication: deprecatedVersionOfMethodX (ระบุว่าคุณสามารถสร้างและเรียกใช้แอปใน Swift บน iOS 7 ได้ซึ่งจะเป็นสถานการณ์ที่พบได้บ่อยมาก)
Gruntcakes

API ที่คุณเป็น / เกี่ยวข้องกับ Apple API หรือไม่
GoZoner

@PotassiumPermanganate - แน่นอนถ้าคำตอบคือ 'ใช่ฉันใช้ Apple APIs' ดังนั้นบางทีเขาสามารถใช้if #available(...)ใน Swift 2.x เพื่อหลีกเลี่ยงการใช้respondsToSelectorครั้งแรก แต่คุณรู้ว่า ( apple.co/1SNGtMQ )
GoZoner

คำตอบ:


170

เป็นที่กล่าวถึงในสวิฟท์มากที่สุดของเวลาที่คุณสามารถบรรลุสิ่งที่คุณต้องการกับ?ผู้ประกอบการ unwrapper ตัวเลือก สิ่งนี้ช่วยให้คุณสามารถเรียกใช้เมธอดบนวัตถุถ้าและถ้าวัตถุนั้นมีอยู่ (ไม่ใช่nil) และวิธีการถูกนำมาใช้

ในกรณีที่คุณยังต้องการrespondsToSelector:มันยังคงเป็นส่วนหนึ่งของNSObjectโปรโตคอล

หากคุณกำลังเรียกrespondsToSelector:ใช้ประเภท 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 ถึง UIA การจำลองหรือไม่และตอนนี้ฉันก็ไม่มีโชค นี่คือวิธีที่ฉันพยายามตามข้างต้น: ถ้าให้ปัจจุบัน = UIApplication.sharedApplication (). responssToSelector (Selector ("redactedAsStillUnderNDA") อย่างไรก็ตามฉันได้รับข้อผิดพลาดในการคอมไพล์: "ค่าที่ถูกผูกไว้ในการผูกแบบมีเงื่อนไขต้องเป็นประเภทเสริม"
Gruntcakes

4
คุณจะต้องแยกบรรทัดเหล่านั้นออกหรือลบlet x = ส่วนนั้นออก โดยทั่วไปif let x = yโครงสร้างคือการแกะค่าที่ไม่จำเป็น (คล้ายกับ!) เมื่อคุณได้รับBoolกลับมาrespondsToSelectorคอมไพเลอร์ก็บ่นว่าผลลัพธ์นั้นไม่ใช่ตัวเลือกประเภท ( Bool?)
Erik

จะเกิดอะไรขึ้นกับการvarประกาศ? พวกเขายังคงตอบสนองเหมือนเดิมหรือไม่? ใน Objective-C ฉันสามารถทำif ( [obj respondsToSelector:@selector(setSomeVar:)] ) { ... }เพื่อsomeVarอสังหาริมทรัพย์ได้ มันทำงานในลักษณะเดียวกันกับvars ใน Swift หรือไม่
Alejandro Iván

จะเกิดอะไรขึ้นถ้าตัวเลือกมุมมองอินสแตนซ์ตัวควบคุมไม่ได้เป็นศูนย์ แต่ไม่มีการใช้วิธีหรือฟังก์ชั่นในตัวควบคุมนั้น ฉันเดาว่าคุณจะต้องตรวจสอบว่าวัตถุตอบสนองต่อตัวเลือกหรือไม่
Ashish Pisey

ฉันได้รับ "มูลค่าประเภท 'UIViewController' ไม่มีสมาชิก '
responsesToSelector

59

ไม่มีการเปลี่ยน Swift ที่แท้จริง

คุณสามารถเช็คอินด้วยวิธีต่อไปนี้:

someObject.someMethod?()

สิ่งนี้เรียกเมธอดsomeMethodต่อเมื่อมันถูกกำหนดบนอ็อบเจ็กต์someObjectแต่คุณสามารถใช้ได้สำหรับ@objcโปรโตคอลที่ประกาศเมธอดเป็นoptionalเท่านั้น

สวิฟท์เป็นภาษาที่มีความปลอดภัยโดยธรรมชาติดังนั้นทุกครั้งที่คุณเรียกใช้เมธอดสวิฟท์จะต้องรู้วิธีที่มีอยู่ ไม่มีการตรวจสอบรันไทม์ที่เป็นไปได้ คุณไม่สามารถเรียกวิธีการสุ่มบนวัตถุสุ่ม

แม้ใน Obj-C คุณควรหลีกเลี่ยงสิ่งเหล่านี้เมื่อเป็นไปได้เพราะมันไม่ได้เล่นได้ดีกับ ARC (ARC จากนั้นเรียกใช้คำเตือนperformSelector:)

อย่างไรก็ตามเมื่อตรวจสอบ API ที่มีอยู่คุณยังคงสามารถใช้งานrespondsToSelector:ได้แม้ว่า Swift หากคุณกำลังจัดการกับNSObjectอินสแตนซ์:

@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 หรือไม่? สวิฟต์เป็นสิ่งที่ดี แต่ดูเหมือนว่าจะเป็นเรื่องเล็กน้อย
Gruntcakes

@sulthan ฉันไม่แน่ใจว่าคุณมาจากไหนโดยที่คุณไม่ควรใช้respondsToSelector:ตามคำแนะนำของ Apple อย่างชัดเจนว่าคุณควรทำเช่นนั้น ดูที่นี่
David Berry

@ David ใช่คุณพบกรณีหนึ่งในไม่กี่กรณีrespondsToSelector:ที่ดีจริง ๆ แต่ถ้าคุณผ่านการอ้างอิง Swift พวกเขาพูดเกี่ยวกับการใช้คลาสย่อยสำหรับรุ่นของระบบที่แตกต่างกัน
Sulthan

หากไม่ใช่กรณีที่ OP กำลังพูดถึงและพวกเขากำลังพูดถึงกรณีโปรโตคอลที่เป็นตัวเลือกพวกเขายังอนุญาตอย่างชัดเจนสำหรับการใช้การผูกมัดตัวเลือก (ตามที่คุณได้ชี้ให้เห็น) ไม่ว่าจะด้วยวิธีใดการพูดว่า "คุณควรหลีกเลี่ยงการทำในสิ่งที่ Apple บอกให้คุณทำอย่างชัดเจน" ดูเหมือนคำแนะนำที่ไม่ดี Apple มีกลไก "เสมอ" สำหรับการพิจารณาและการใช้งานฟังก์ชั่นเสริมในเวลาทำงาน
David Berry

1
@Honey In Swift โดยทั่วไปเราใช้คำสั่งความพร้อมใช้งานแทนเช่นif #available(iOS 10) {แล้วเรียกวิธีการโดยตรง
Sulthan

40

อัปเดต 20 มีนาคม 2560 สำหรับไวยากรณ์ของ Swift 3:

หากคุณไม่สนใจว่าจะมีวิธีการอื่นหรือไม่เพียงแค่โทรหา delegate?.optionalMethod?()

มิฉะนั้นการใช้guardอาจเป็นวิธีที่ดีที่สุด:

weak var delegate: SomeDelegateWithOptionals?

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

คำตอบเดิม:

คุณสามารถใช้วิธี "ถ้าให้" เพื่อทดสอบโปรโตคอลเพิ่มเติมดังนี้:

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
ถ้าปล่อยให้ theMethod = มอบหมาย?. theOptionalProtocolMethod
john07

1
ตัวอย่างของไวยากรณ์เลือกเมื่อมีผู้สมัครหลายlet theMethod = delegate.userNotificationCenter(_:willPresent:withCompletionHandler:)
Cœur

11

ดูเหมือนว่าคุณจะต้องกำหนดโปรโตคอลของคุณเป็น subprotocol ของ NSObjectProtocol ... จากนั้นคุณจะได้รับเมธอด responsesToSelector

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

โปรดทราบว่าการระบุ @objc เพียงอย่างเดียวนั้นไม่เพียงพอ คุณควรระวังด้วยว่าตัวแทนที่แท้จริงคือ subclass ของ NSObject - ซึ่งใน Swift อาจไม่เป็นเช่นนั้น


10

หากวิธีที่คุณกำลังทดสอบถูกกำหนดเป็นวิธีเลือกในโปรโตคอล@objc (ซึ่งฟังดูเป็นกรณีของคุณ) ให้ใช้รูปแบบการผูกมัดเสริมเป็น:

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

เมื่อเมธอดถูกประกาศว่าส่งคืนให้Voidใช้:

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

ดู:

“ วิธีการโทรด้วยการผูกมัดเสริม”
ข้อความที่ตัดตอนมาจาก: Apple Inc. “ ภาษาการเขียนโปรแกรม Swift”
iBooks https://itun.es/us/jEUH0.l


สิ่งนี้จะใช้ได้ถ้าวิธีคืนค่าบางอย่างแล้วถ้าเป็นโมฆะจะเป็นอย่างไร
Gruntcakes

1
หากโมฆะใช้ง่ายif object.method?(args) { ... }- การเรียกใช้เมธอดเมื่อมีอยู่จะกลับมาVoidซึ่งไม่ใช่nil
GoZoner

1
อย่างไรก็ตามผลลัพธ์ใน "Type Void ไม่สอดคล้องกับโปรโตคอล" ค่าตรรกะ ""
Gruntcakes

โปรดดูเอกสารอ้างอิง (หน้า 311-312)
GoZoner

"หากวิธีการที่คุณทดสอบถูกกำหนดเป็นวิธีเลือกในโปรโตคอล @objc" หรือหากobjectมีประเภทAnyObjectคุณสามารถทดสอบวิธี @objc ใด ๆ ก็ได้
newacct

9

ฟังก์ชั่นเป็นประเภทเฟิร์สคลาสใน Swift ดังนั้นคุณสามารถตรวจสอบว่ามีการใช้งานฟังก์ชั่นเสริมที่กำหนดในโปรโตคอลโดยการเปรียบเทียบกับศูนย์หรือไม่:

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

1
เกิดอะไรขึ้นถ้าวิธีการที่มากเกินไป? เราจะตรวจสอบรายการที่ต้องการได้อย่างไร
Nuno Gonçalves


7

ใน 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
    }
}

ฉันคิดว่านั่นเป็นทางออกที่ดีกว่าการใช้respondsToSelectorวิธีการใน Swift นั่นเป็นเพราะการเลือกตัวเลือกไม่ใช่วิธีที่ดีที่สุดในการแก้ไขปัญหานี้ (การตรวจสอบความพร้อม) ในตอนแรก
JanApotheker

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 ซึ่งเป็นวิธีที่รวดเร็ว
  • หากคุณไม่ต้องการทำสิ่งใดเมื่อผู้รับมอบสิทธิ์เป็นศูนย์หรือไม่ได้ใช้วิธีการให้ใช้ตัวเลือกที่ 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)
}

เกิดอะไรขึ้นถ้าวิธีการที่มากเกินไป? เราจะทำอย่างนั้นได้อย่างไร?
Nuno Gonçalves

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 หรือไม่
Gruntcakes

2
คุณต้องมีการ?หลังจากimportantMethod
newacct
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.