स्विफ्ट में, मैं एक विशिष्ट प्रकार के चर को कैसे घोषित कर सकता हूं जो एक या अधिक प्रोटोकॉल के अनुरूप है?


96

स्विफ्ट में मैं स्पष्ट रूप से इस प्रकार घोषित करके एक चर का प्रकार निर्धारित कर सकता हूं:

var object: TYPE_NAME

यदि हम इसे एक कदम आगे ले जाना चाहते हैं और एक चर की घोषणा करते हैं जो कई प्रोटोकॉल के अनुरूप होता है तो हम protocolघोषणापत्र का उपयोग कर सकते हैं :

var object: protocol<ProtocolOne,ProtocolTwo>//etc

क्या होगा यदि मैं एक ऐसी वस्तु की घोषणा करना चाहूंगा जो एक या अधिक प्रोटोकॉल के अनुरूप हो और एक विशिष्ट आधार वर्ग प्रकार की भी हो? ऑब्जेक्टिव-सी समतुल्य इस तरह दिखेगा:

NSSomething<ABCProtocolOne,ABCProtocolTwo> * object = ...;

स्विफ्ट में मैं उम्मीद करूंगा कि यह इस तरह दिखे:

var object: TYPE_NAME,ProtocolOne//etc

यह हमें आधार प्रकार के कार्यान्वयन के साथ-साथ प्रोटोकॉल में परिभाषित अतिरिक्त इंटरफ़ेस के कार्यान्वयन में सक्षम होने का लचीलापन देता है।

क्या कोई और स्पष्ट तरीका है जो मुझे याद आ रहा है?

उदाहरण

एक उदाहरण के रूप में, कहो कि मेरे पास एक UITableViewCellकारखाना है जो एक प्रोटोकॉल के अनुरूप कोशिकाओं को वापस करने के लिए जिम्मेदार है। हम एक जेनेरिक फ़ंक्शन को आसानी से सेटअप कर सकते हैं जो प्रोटोकॉल के अनुरूप कोशिकाओं को लौटाता है:

class CellFactory {
    class func createCellForItem<T: UITableViewCell where T:MyProtocol >(item: SpecialItem,tableView: UITableView) -> T {
        //etc
    }
}

बाद में मैं इन कोशिकाओं को टाइप और प्रोटोकॉल दोनों का लाभ उठाते हुए अलग करना चाहता हूं

var cell: MyProtocol = CellFactory.createCellForItem(somethingAtIndexPath) as UITableViewCell

यह एक त्रुटि देता है क्योंकि एक टेबल व्यू सेल प्रोटोकॉल के अनुरूप नहीं है ...

मैं यह बताना चाहूंगा कि सेल एक है UITableViewCellऔर MyProtocolचर घोषणा में अनुरूप है ?

औचित्य

यदि आप फ़ैक्टरी पैटर्न से परिचित हैं, तो यह एक विशेष वर्ग की वस्तुओं को वापस करने में सक्षम होने के संदर्भ में समझ में आता है जो एक निश्चित इंटरफ़ेस को लागू करते हैं।

मेरे उदाहरण की तरह, कभी-कभी हम ऐसे इंटरफेस को परिभाषित करना पसंद करते हैं जो किसी विशेष वस्तु पर लागू होने पर समझ में आता है। टेबल व्यू सेल का मेरा उदाहरण एक ऐसा औचित्य है।

जब भी आपूर्ति की गई प्रकार उल्लिखित इंटरफ़ेस के अनुरूप नहीं होती है, फैक्ट्री रिटर्न करती है और इसलिए मैं बेस क्लास प्रकार और घोषित प्रोटोकॉल इंटरफ़ेस दोनों के साथ बातचीत करने में लचीलापन चाहूंगा।


क्षमा करें, लेकिन इस तेजी में क्या बात है। प्रकार पहले से ही जानते हैं कि वे किस प्रोटोकॉल के अनुरूप हैं। केवल प्रकार का उपयोग न करें?
कर्स्टन

1
@ कर्स्टन तब तक नहीं जब तक कि प्रकार एक कारखाने से वापस नहीं किया जाता है और इस प्रकार एक सामान्य आधार वर्ग के साथ एक सामान्य प्रकार है
डैनियल गैलास्को

कृपया यदि संभव हो तो एक उदाहरण दें।
Kirsteins

NSSomething<ABCProtocolOne,ABCProtocolTwo> * object = ...;। यह ऑब्जेक्ट काफी बेकार लगता है क्योंकि NSSomethingपहले से ही पता है कि यह किसके अनुरूप है। यदि यह प्रोटोकॉल में से किसी एक के अनुरूप नहीं है, <>तो आपको unrecognised selector ...क्रैश मिल जाएगा । यह किसी भी प्रकार की सुरक्षा प्रदान नहीं करता है।
कर्स्टन

@ कर्स्टन कृपया मेरा उदाहरण फिर से देखें, इसका उपयोग तब किया जाता है जब आप जानते हैं कि आपके कारखाने का उद्देश्य जिस वस्तु से होता है वह किसी विशिष्ट प्रोटोकॉल के अनुरूप एक विशेष आधार वर्ग का होता है
डैनियल गैलास्को

जवाबों:


72

स्विफ्ट 4 में अब एक वेरिएबल घोषित करना संभव है जो एक प्रकार का उपवर्ग है और एक ही समय में एक या अधिक प्रोटोकॉल लागू करता है।

var myVariable: MyClass & MyProtocol & MySecondProtocol

एक वैकल्पिक चर करने के लिए:

var myVariable: (MyClass & MyProtocol & MySecondProtocol)?

या एक विधि के पैरामीटर के रूप में:

func shakeEm(controls: [UIControl & Shakeable]) {}

Apple ने इसकी घोषणा WWDC 2017 में सत्र 402: स्विफ्ट में व्हाट्सएप पर की

दूसरा, मैं कक्षाओं और प्रोटोकॉल की रचना के बारे में बात करना चाहता हूं। तो, यहाँ मैंने एक UI तत्व के लिए यह शेकेबल प्रोटोकॉल पेश किया है जो थोड़ा ध्यान आकर्षित कर सकता है। और मैं आगे बढ़ा हूँ और कुछ UIKit कक्षाओं को बढ़ाया है जो वास्तव में इस शेक की कार्यक्षमता प्रदान करते हैं। और अब मैं कुछ लिखना चाहता हूं जो सरल लगता है। मैं केवल एक फ़ंक्शन लिखना चाहता हूं जो नियंत्रण का एक गुच्छा लेता है जो कि शेकेबल हैं और जो उन पर ध्यान आकर्षित करने में सक्षम हैं उन्हें हिलाता है। इस सरणी में मैं यहां किस प्रकार लिख सकता हूं? यह वास्तव में निराशा और मुश्किल है। इसलिए, मैं UI नियंत्रण का उपयोग करने का प्रयास कर सकता हूं। लेकिन इस गेम में सभी UI कंट्रोल शेकेबल नहीं हैं। मैं shakable की कोशिश कर सकता था, लेकिन सभी shakables UI नियंत्रण नहीं हैं। और स्विफ्ट 3 में वास्तव में इसका प्रतिनिधित्व करने का कोई अच्छा तरीका नहीं है।स्विफ्ट 4 किसी भी संख्या के प्रोटोकॉल के साथ एक वर्ग की रचना करने की धारणा का परिचय देता है।


3
बस स्विफ्ट इवोल्यूशन प्रपोजल github.com/apple/swift-evolution/blob/master/proposals/… के
डेनियल गैलास्को

धन्यवाद फिलिप्पुस!
बजे उमर अलबिक

यदि इस प्रकार के वैकल्पिक चर की आवश्यकता हो तो क्या होगा?
व्याचेस्लाव गेरिकोव

2
@VyachaslavGerchicov: आप इसके चारों ओर कोष्ठक लगा सकते हैं और फिर इस तरह प्रश्न चिह्न लगा सकते हैं: var myVariable: (MyClass & MyProtocol & MySecondProtocol)?
फिलिप ओटो

30

आप चर की तरह घोषित नहीं कर सकते

var object:Base,protocol<ProtocolOne,ProtocolTwo> = ...

न ही फ़ंक्शन रिटर्न प्रकार की घोषणा करें

func someFunc() -> Base,protocol<MyProtocol,Protocol2> { ... }

आप इस तरह एक फ़ंक्शन पैरामीटर के रूप में घोषित कर सकते हैं, लेकिन यह मूल रूप से अप-कास्टिंग है।

func someFunc<T:Base where T:protocol<MyProtocol1,MyProtocol2>>(val:T) {
    // here, `val` is guaranteed to be `Base` and conforms `MyProtocol` and `MyProtocol2`
}

class SubClass:BaseClass, MyProtocol1, MyProtocol2 {
   //...
}

let val = SubClass()
someFunc(val)

अब तक, आप सभी कर सकते हैं जैसे:

class CellFactory {
    class func createCellForItem(item: SpecialItem) -> UITableViewCell {
        return ... // any UITableViewCell subclass
    }
}

let cell = CellFactory.createCellForItem(special)
if let asProtocol = cell as? protocol<MyProtocol1,MyProtocol2> {
    asProtocol.protocolMethod()
    cell.cellMethod()
}

इसके साथ, तकनीकी रूप cellसे समान है asProtocol

लेकिन, संकलक के लिए, cellकेवल इंटरफ़ेस है UITableViewCell, जबकि asProtocolकेवल प्रोटोकॉल इंटरफ़ेस है। इसलिए, जब आप UITableViewCellविधियों को कॉल करना चाहते हैं, तो आपको cellचर का उपयोग करना होगा । जब आप प्रोटोकॉल विधि को कॉल करना चाहते हैं, तो asProtocolचर का उपयोग करें।

यदि आप सुनिश्चित हैं कि सेल उन प्रोटोकॉल के अनुरूप है जिनका आपको उपयोग करने की आवश्यकता नहीं है if let ... as? ... {}। पसंद:

let cell = CellFactory.createCellForItem(special)
let asProtocol = cell as protocol<MyProtocol1,MyProtocol2>

चूंकि कारखाना रिटर्न प्रकार निर्दिष्ट करता है जो मुझे तकनीकी रूप से वैकल्पिक कलाकारों के प्रदर्शन की आवश्यकता नहीं है? जहाँ मैं स्पष्ट रूप से प्रोटोकॉल की घोषणा कर रहा हूँ, वहाँ टाइपिंग करने के लिए मैं अंतर्निहित टाइपिंग पर भरोसा कर सकता हूँ?
डैनियल गैलास्को

मुझे समझ नहीं आ रहा है कि आपका क्या मतलब है, मेरे बुरे अंग्रेजी कौशल के लिए क्षमा करें। यदि आप इसके बारे में कह रहे हैं -> UITableViewCell<MyProtocol>, तो यह अमान्य है, क्योंकि UITableViewCellयह सामान्य प्रकार नहीं है। मुझे लगता है कि यह भी संकलन नहीं है।
रिण्टारो

Im आपके सामान्य कार्यान्वयन का उल्लेख नहीं कर रहा है, बल्कि आपके उदाहरण कार्यान्वयन का उदाहरण है। जहाँ आप कहते हैं कि asProtocol = ...
डैनियल गैलास्को

या, मैं बस कर सकता है: var सेल: प्रोटोकॉल <प्रोटोकॉलऑन, ProtocolTwo> = someObject को UITableViewCell के रूप में और एक ही चर में दोनों का लाभ प्राप्त करें
डैनियल गैलास्को

2
मुझे ऐसा नहीं लगता। यहां तक ​​कि अगर आप ऐसा कर सकते हैं, तो cellकेवल प्रोटोकॉल विधियाँ हैं (संकलक के लिए)।
रिण्टारो १६'१४

2

दुर्भाग्य से, स्विफ्ट ऑब्जेक्ट स्तर प्रोटोकॉल अनुरूपता का समर्थन नहीं करता है। हालाँकि, कुछ अजीब-सा काम है, जो आपके उद्देश्यों की पूर्ति कर सकता है।

struct VCWithSomeProtocol {
    let protocol: SomeProtocol
    let viewController: UIViewController

    init<T: UIViewController>(vc: T) where T: SomeProtocol {
        self.protocol = vc
        self.viewController = vc
    }
}

फिर, कहीं भी आपको कुछ भी करने की ज़रूरत है जो कि UIViewController है, आप संरचना के .viewController पहलू तक पहुँच सकते हैं और कुछ भी आपको प्रोटोकॉल पहलू की आवश्यकता होगी, आप .protocol का संदर्भ लेंगे।

उदाहरण के लिए:

class SomeClass {
   let mySpecialViewController: VCWithSomeProtocol

   init<T: UIViewController>(injectedViewController: T) where T: SomeProtocol {
       self.mySpecialViewController = VCWithSomeProtocol(vc: injectedViewController)
   }
}

अब कभी भी आपको UIViewController से संबंधित कुछ भी करने के लिए mySpecialViewController की आवश्यकता होती है, आप सिर्फ mySpecialViewController.viewController का संदर्भ लेते हैं और जब भी आपको कुछ प्रोटोकॉल फ़ंक्शन करने की आवश्यकता होती है, तो आप mySpecialViewController.protocol का संदर्भ लेते हैं।

उम्मीद है कि स्विफ्ट 4 हमें भविष्य में इससे जुड़े प्रोटोकॉल के साथ एक वस्तु घोषित करने की अनुमति देगा। लेकिन अभी के लिए, यह काम करता है।

उम्मीद है की यह मदद करेगा!


1

संपादित करें: मुझसे गलती हुई थी , लेकिन अगर किसी और ने मेरी तरह इस गलतफहमी को पढ़ा, तो मैं इस जवाब को वहीं छोड़ देता हूं। ओपी ने किसी दिए गए उपवर्ग की वस्तु के प्रोटोकॉल अनुरूपता की जाँच करने के बारे में पूछा, और यह एक अन्य कहानी है जो स्वीकृत उत्तर दिखाता है। यह जवाब बेस क्लास के लिए प्रोटोकॉल कन्फर्मेशन के बारे में बात करता है।

शायद मैं गलत हूं, लेकिन क्या आप UITableCellViewवर्ग में प्रोटोकॉल अनुरूपता को जोड़ने की बात नहीं कर रहे हैं ? प्रोटोकॉल उस मामले में बेस क्लास तक बढ़ा है, न कि ऑब्जेक्ट। एक विस्तार के साथ प्रोटोकॉल गोद लेने पर एप्पल के प्रलेखन देखें जो आपके मामले में कुछ इस तरह होगा:

extension UITableCellView : ProtocolOne {}

// Or alternatively if you need to add a method, protocolMethod()
extension UITableCellView : ProcotolTwo {
   func protocolTwoMethod() -> String {
     return "Compliant method"
   }
}

पहले से संदर्भित स्विफ्ट प्रलेखन के अलावा, आगे के उदाहरणों के साथ असंगत प्रकारों के लिए नैट कुक्स लेख जेनेरिक फ़ंक्शन भी देखें ।

यह हमें आधार प्रकार के कार्यान्वयन के साथ-साथ प्रोटोकॉल में परिभाषित अतिरिक्त इंटरफ़ेस के कार्यान्वयन में सक्षम होने का लचीलापन देता है।

क्या कोई और स्पष्ट तरीका है जो मुझे याद आ रहा है?

प्रोटोकॉल अडॉप्शन सिर्फ यही करेगा, किसी दिए गए प्रोटोकॉल का पालन करें। लेकिन प्रतिकूल पक्ष से अवगत रहें, कि किसी दिए गए प्रोटोकॉल प्रकार का एक संस्करण प्रोटोकॉल के बाहर कुछ भी नहीं जानता है। लेकिन एक प्रोटोकॉल को परिभाषित करके इसे दरकिनार किया जा सकता है जिसमें सभी आवश्यक तरीके / चर / हैं ...

जब भी आपूर्ति की गई प्रकार उल्लिखित इंटरफ़ेस के अनुरूप नहीं होती है, फैक्ट्री रिटर्न करती है और इसलिए मैं बेस क्लास प्रकार और घोषित प्रोटोकॉल इंटरफ़ेस दोनों के साथ बातचीत करने में लचीलापन चाहूंगा।

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


1
यह नहीं है कि मैं किस बारे में बात कर रहा हूं, लेकिन धन्यवाद :) मैं अपनी कक्षा और एक विशिष्ट प्रोटोकॉल दोनों के माध्यम से किसी वस्तु के साथ इंटरफ़ेस करने में सक्षम होना चाहता था। जैसे ओब्ज-सी में मैं कैसे कर सकता हूं NSObject <MyProtocol> obj = ... यह कहने की जरूरत नहीं है कि यह तेजी से नहीं किया जा सकता है, आपको इसके प्रोटोकॉल के लिए ऑब्जेक्ट डालना होगा
डैनियल गैलास्को

0

जब मैं स्टोरीबोर्ड में अपने जेनेरिक इंटरेक्टर कनेक्शन को जोड़ने की कोशिश कर रहा था, तो एक बार ऐसी ही स्थिति आई थी (आईबी आपको प्रोटोकॉल, केवल ऑब्जेक्ट इंस्टेंसेस से आउटलेट कनेक्ट करने की अनुमति नहीं देगा), जो मुझे बस एक निजी गणना के साथ बेस क्लास पब्लिक आइवर को मास्क करके मिला है। संपत्ति। हालांकि यह किसी को प्रति से अधिक अवैध कार्य करने से नहीं रोकता है, लेकिन यह रनटाइम पर गैर-अनुरूपण उदाहरण के साथ किसी भी अवांछित बातचीत को सुरक्षित रूप से रोकने के लिए एक सुविधाजनक तरीका प्रदान करता है। (यानी उन प्रत्यायोजित विधियों को कॉल करने से रोकें जो प्रोटोकॉल के अनुरूप नहीं हैं।)

उदाहरण:

@objc protocol SomeInteractorInputProtocol {
    func getSomeString()
}

@objc protocol SomeInteractorOutputProtocol {
    optional func receiveSomeString(value:String)
}

@objc class SomeInteractor: NSObject, SomeInteractorInputProtocol {

    @IBOutlet var outputReceiver : AnyObject? = nil

    private var protocolOutputReceiver : SomeInteractorOutputProtocol? {
        get { return self.outputReceiver as? SomeInteractorOutputProtocol }
    }

    func getSomeString() {
        let aString = "This is some string."
        self.protocolOutputReceiver?.receiveSomeString?(aString)
    }
}

"OutputReceiver" को वैकल्पिक घोषित किया गया है, जैसा कि निजी "ProtocolOutputReceiver" है। हमेशा बाद में (गणना की गई संपत्ति) के माध्यम से outputReceiver (उर्फ प्रतिनिधि) तक पहुंचकर, मैं प्रभावी रूप से किसी भी ऑब्जेक्ट को फ़िल्टर करता हूं जो प्रोटोकॉल के अनुरूप नहीं है। अब मैं बस वैकल्पिक चाइनिंग का उपयोग सुरक्षित रूप से प्रतिनिधि ऑब्जेक्ट को कॉल करने के लिए कर सकता हूं चाहे वह प्रोटोकॉल को लागू करता है या नहीं।

इसे अपनी स्थिति पर लागू करने के लिए, आप सार्वजनिक ivar प्रकार "YourBaseClass?" (AnyObject के विपरीत), और प्रोटोकॉल अनुरूपता को लागू करने के लिए निजी गणना की गई संपत्ति का उपयोग करें। FWIW।

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