मैं स्विफ्ट # सिलेक्टर सिंटैक्स के साथ "त्रुटि का अस्पष्ट उपयोग" कैसे हल करूं?


79

[ नोट: यह सवाल मूल रूप से स्विफ्ट 2.2 के तहत तैयार किया गया था। यह स्विफ्ट 4 के लिए संशोधित किया गया है, जिसमें दो महत्वपूर्ण भाषा परिवर्तन शामिल हैं: पहला विधि पैरामीटर बाहरी अब स्वचालित रूप से दबा हुआ नहीं है, और एक चयनकर्ता को स्पष्ट रूप से ऑब्जेक्टिव-सी के संपर्क में होना चाहिए।]

मान लीजिए कि मेरी कक्षा में ये दो विधियाँ हैं:

@objc func test() {}
@objc func test(_ sender:AnyObject?) {}

अब मैं इन तरीकों #selectorमें से पहले के लिए एक चयनकर्ता बनाने के लिए स्विफ्ट 2.2 के नए सिंटैक्स का उपयोग करना चाहता हूं func test()। मैं यह कैसे करुं? जब मैं यह कोशिश करता हूं:

let selector = #selector(test) // error

... मुझे एक त्रुटि मिलती है, "अस्पष्ट उपयोग test()।" लेकिन अगर मैं यह कहूं:

let selector = #selector(test(_:)) // ok, but...

... त्रुटि दूर हो जाती है, लेकिन मैं अब गलत पद्धति का उल्लेख कर रहा हूं , एक पैरामीटर के साथ । मैं किसी भी पैरामीटर के बिना एक को संदर्भित करना चाहता हूं । मैं यह कैसे करुं?

[नोट: उदाहरण कृत्रिम नहीं है। NSObject में ऑब्जेक्टिव-सी copyऔर copy:इंस्टेंस दोनों तरीके हैं, स्विफ्ट copy()और copy(sender:AnyObject?); इसलिए समस्या आसानी से वास्तविक जीवन में उत्पन्न हो सकती है।]

जवाबों:


110

[ नोट: यह उत्तर मूल रूप से स्विफ्ट 2.2 के तहत तैयार किया गया था। यह स्विफ्ट 4 के लिए संशोधित किया गया है, जिसमें दो महत्वपूर्ण भाषा परिवर्तन शामिल हैं: पहला विधि पैरामीटर बाहरी अब स्वचालित रूप से दबा हुआ नहीं है, और एक चयनकर्ता को स्पष्ट रूप से ऑब्जेक्टिव-सी के संपर्क में होना चाहिए।]

आप अपने कार्य संदर्भ को सही विधि हस्ताक्षर में डालकर इस समस्या को हल कर सकते हैं :

let selector = #selector(test as () -> Void)

(हालांकि, मेरी राय में, आपको ऐसा नहीं करना चाहिए। मैं इस स्थिति को एक बग के रूप में मानता हूं, यह दर्शाता है कि कार्यों का संदर्भ देने के लिए स्विफ्ट का वाक्यविन्यास अपर्याप्त है। मैंने एक बग रिपोर्ट दर्ज की, लेकिन कोई फायदा नहीं हुआ।)


बस नए #selectorवाक्यविन्यास को संक्षेप में प्रस्तुत करने के लिए :

इस सिंटैक्स का उद्देश्य ऑल-बहुत-सामान्य रनटाइम क्रैश (आमतौर पर "अपरिचित चयनकर्ता") को रोकना है जो चयनकर्ता को शाब्दिक स्ट्रिंग के रूप में आपूर्ति करते समय उत्पन्न हो सकता है। #selector()एक फ़ंक्शन संदर्भ लेता है , और कंपाइलर जांच करेगा कि फ़ंक्शन वास्तव में मौजूद है और आपके लिए ऑब्जेक्टिव-सी चयनकर्ता के संदर्भ को हल करेगा। इस प्रकार, आप आसानी से कोई गलती नहीं कर सकते।

( संपादित करें: ठीक है, हाँ, आप कर सकते हैं। आप एक पूर्ण लंकहेड हो सकते हैं और लक्ष्य को ऐसे उदाहरण पर सेट कर सकते हैं, जो इसके द्वारा निर्दिष्ट क्रिया संदेश को कार्यान्वित नहीं करता है #selector। संकलक आपको रोक नहीं पाएगा और आप जैसे दुर्घटनाग्रस्त हो जाएंगे। अच्छे पुराने दिन। आहें ...)

एक फ़ंक्शन संदर्भ तीन में से किसी भी रूप में दिखाई दे सकता है:

  • फ़ंक्शन का नंगे नाम । यह पर्याप्त है यदि फ़ंक्शन असंदिग्ध है। इस प्रकार, उदाहरण के लिए:

    @objc func test(_ sender:AnyObject?) {}
    func makeSelector() {
        let selector = #selector(test)
    }
    

    केवल एक ही testविधि है, इसलिए यह #selectorइसे संदर्भित करता है भले ही यह एक पैरामीटर लेता है और पैरामीटर का #selectorउल्लेख नहीं करता है। पर्दे के पीछे सुलझा हुआ ऑब्जेक्टिव-सी सिलेक्टर, अभी भी सही ढंग से "test:"(कोलोन के साथ, एक पैरामीटर को दर्शाता है) होगा।

  • फ़ंक्शन का नाम इसके बाकी हस्ताक्षर के साथ । उदाहरण के लिए:

    func test() {}
    func test(_ sender:AnyObject?) {}
    func makeSelector() {
        let selector = #selector(test(_:))
    }
    

    हमारे पास दो testतरीके हैं, इसलिए हमें अंतर करने की आवश्यकता है; अंकन test(_:)पर ले कर दूसरे एक, एक पैरामीटर के साथ एक।

  • अपने शेष हस्ताक्षर के साथ या बिना फ़ंक्शन का नाम, साथ ही मापदंडों के प्रकार को दिखाने के लिए एक कास्ट । इस प्रकार:

    @objc func test(_ integer:Int) {}
    @nonobjc func test(_ string:String) {}
    func makeSelector() {
        let selector1 = #selector(test as (Int) -> Void)
        // or:
        let selector2 = #selector(test(_:) as (Int) -> Void)
    }
    

    यहां, हमने ओवरलोड किया है test(_:) । ओवरलोडिंग, ऑब्जेक्टिव-सी के संपर्क में नहीं किया जा सकता क्योंकि ऑब्जेक्टिव-सी अधिक भार की अनुमति नहीं देता है, तो उनमें से केवल एक सामने आ रहा है, और हम केवल यह है कि एक के लिए एक चयनकर्ता फार्म कर सकते हैं है अवगत कराया, क्योंकि चयनकर्ताओं एक ऑब्जेक्टिव-सी विशेषता है । लेकिन हमें अभी भी स्पष्ट होना चाहिए जहां तक ​​स्विफ्ट का संबंध है, और कास्ट ऐसा करता है।

    (यह भाषाई विशेषता है जिसका उपयोग किया जाता है - दुरुपयोग, मेरी राय में - ऊपर दिए गए उत्तर के आधार के रूप में)

इसके अलावा, आपको स्विफ्ट को फ़ंक्शन संदर्भ को यह बताने में मदद करनी पड़ सकती है कि फ़ंक्शन किस वर्ग में है:

  • यदि कक्षा इस एक के समान है, या इस एक से सुपरक्लास श्रृंखला ऊपर है, तो कोई और रिज़ॉल्यूशन आमतौर पर आवश्यक नहीं है (जैसा कि ऊपर के उदाहरणों में दिखाया गया है); वैकल्पिक रूप से, आप कह सकते हैं self, डॉट-नोटेशन (उदाहरण के लिए #selector(self.test), और कुछ स्थितियों में आपको ऐसा करना पड़ सकता है।

  • अन्यथा, आप या तो एक उदाहरण के लिए संदर्भ का उपयोग करते हैं जिसके लिए विधि को लागू किया जाता है, डॉट-नोटेशन के साथ, जैसा कि इस वास्तविक-जीवन उदाहरण में ( self.mpएक MPMusicPlayerController है):

    let pause = UIBarButtonItem(barButtonSystemItem: .pause, 
        target: self.mp, action: #selector(self.mp.pause))
    

    ... या आप डॉट-नोटेशन के साथ वर्ग के नाम का उपयोग कर सकते हैं :

    class ClassA : NSObject {
        @objc func test() {}
    }
    class ClassB {
        func makeSelector() {
            let selector = #selector(ClassA.test)
        }
    }
    

    (यह एक जिज्ञासु संकेतन लगता है, क्योंकि ऐसा लगता है कि आप कह रहे हैं कि testउदाहरण विधि के बजाय एक वर्ग विधि है, लेकिन यह सही ढंग से एक चयनकर्ता को फिर भी हल किया जाएगा, जो कि सभी मामलों में है।)


2
हाय @ सुल्तान, आपसे सुनकर अच्छा लगा। - नहीं, यह एक फ़ंक्शन कॉल की व्याख्या करता है। बस सीधे अवधारणा को "कोई मापदंडों के साथ एक" नोट करने का कोई तरीका नहीं है। यह एक छेद है; वे इसे बिना सोचे समझे आगे बढ़ा देते हैं, जैसे (वैसे भी) ...
मैट

4
@ सुल्तान जैसा कि मुझे डर था, बग की रिपोर्ट "इरादा के अनुसार काम करती है" वापस आ गई। तो मेरा उत्तर है उत्तर: आप है उपयोग करने के लिए कोई पैरामीटर संस्करण निर्दिष्ट करने के लिए आदेश में अंकन। as
मैट

1
स्विफ्ट में कोड करने के लिए "अद्भुत" अनुभव का एक और आकर्षण है।
सिंह नटण

5
वर्तमान स्विफ्ट 3 के साथ, आपको तर्क सूची को कोष्ठक में रखना होगा let selector = #selector(test as (Void) -> Void):।
मार्टिन आर

1
शायद सबसे अच्छी जगह नहीं, लेकिन स्विफ्ट 3 में पसंदीदा सिंटैक्स क्या होगा? test as (Void) -> Voidया छोटे वाक्यविन्यास test as () -> ()?
डैम

1

मैं एक लापता छूट जोड़ना चाहता हूं: कक्षा के बाहर से एक इंस्टेंस विधि तक पहुंचना।

class Foo {
    @objc func test() {}
    @objc func test(_ sender: AnyObject?) {}
}

कक्षा के दृष्टिकोण से test()विधि का पूर्ण हस्ताक्षर है (Foo) -> () -> Void, जिसे प्राप्त करने के लिए आपको निर्दिष्ट करना होगा Selector

#selector(Foo.test as (Foo) -> () -> Void)
#selector(Foo.test(_:))

वैकल्पिक रूप से आप एक उदाहरण के संदर्भ में बता सकते हैं Selectorजैसा कि मूल उत्तर में दिखाया गया है।

let foo = Foo()
#selector(foo.test as () -> Void)
#selector(foo.test(_:))

हां, संकेतन Foo.xxxपहले से ही अजीब है, क्योंकि ये बाहरी रूप से कक्षा के तरीके नहीं हैं। तो ऐसा लगता है कि संकलक आपको एक पास देता है, लेकिन केवल अगर कोई अस्पष्टता नहीं है। यदि अस्पष्टता है, तो आपको अपनी आस्तीन को वापस रोल करना होगा और लंबी संकेतन का उपयोग करना होगा, जो कानूनी और सटीक है क्योंकि एक उदाहरण विधि "गुप्त रूप से" एक करीबी वर्ग विधि है। बचे हुए किनारे के मामले की बहुत बारीक खोज!
मैट

0

मेरे मामले में (Xcode 11.3.1) केवल डीबगिंग करते समय lldb का उपयोग करते समय त्रुटि थी। इसे चलाते समय यह ठीक से काम करता है।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.