सरणी प्रकार और फ़ंक्शन पैरामीटर के रूप में स्विफ्ट में प्रोटोकॉल का उपयोग


136

मैं एक ऐसा वर्ग बनाना चाहता हूं जो एक निश्चित प्रोटोकॉल के अनुरूप वस्तुओं को संग्रहीत कर सके। वस्तुओं को टाइप की गई सरणी में संग्रहित किया जाना चाहिए। स्विफ्ट प्रलेखन प्रोटोकॉल के अनुसार प्रकार के रूप में इस्तेमाल किया जा सकता है: 

क्योंकि यह एक प्रकार है, आप कई स्थानों पर एक प्रोटोकॉल का उपयोग कर सकते हैं जहां अन्य प्रकार की अनुमति है, जिसमें शामिल हैं:

  • एक फ़ंक्शन, विधि या इनिशलाइज़र में पैरामीटर प्रकार या वापसी प्रकार के रूप में
  • एक स्थिर, चर या संपत्ति के प्रकार के रूप में
  • एक सरणी, शब्दकोश, या अन्य कंटेनर में आइटम के प्रकार के रूप में

हालांकि निम्नलिखित कंपाइलर त्रुटियां उत्पन्न करता है:

प्रोटोकॉल 'SomeProtocol' को केवल एक सामान्य बाधा के रूप में इस्तेमाल किया जा सकता है क्योंकि इसमें स्व या संबद्ध प्रकार की आवश्यकताएं होती हैं

आप इसे कैसे हल करने वाले हैं:

protocol SomeProtocol: Equatable {
    func bla()
}

class SomeClass {
    
    var protocols = [SomeProtocol]()
    
    func addElement(element: SomeProtocol) {
        self.protocols.append(element)
    }
    
    func removeElement(element: SomeProtocol) {
        if let index = find(self.protocols, element) {
            self.protocols.removeAtIndex(index)
        }
    }
}

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

जवाबों:


48

आपने स्विफ्ट में प्रोटोकॉल के साथ एक समस्या का एक संस्करण मारा है जिसके लिए कोई अच्छा समाधान अभी तक मौजूद नहीं है।

यह देखने के लिए कि क्या यह स्विफ्ट में सॉर्ट किया गया है, एक्सटेंडिंग एरे को भी देखें , इसमें सुझाव दिया गया है कि इसके चारों ओर कैसे काम किया जाए जो आपकी विशिष्ट समस्या के लिए उपयुक्त हो (आपका प्रश्न बहुत ही सामान्य है, हो सकता है कि आप इन उत्तरों का उपयोग करके वर्कअराउंड प्राप्त कर सकें)।


1
मुझे लगता है कि यह फिलहाल सही जवाब है। नैट का समाधान काम कर रहा है लेकिन मेरी समस्या का पूरी तरह से समाधान नहीं करता है।
स्नोड

32

आप एक सामान्य वर्ग बनाना चाहते हैं, एक प्रकार की बाधा के साथ जिसके लिए उपयोग की जाने वाली कक्षाओं की आवश्यकता होती है SomeProtocol, जैसे:

class SomeClass<T: SomeProtocol> {
    typealias ElementType = T
    var protocols = [ElementType]()

    func addElement(element: ElementType) {
        self.protocols.append(element)
    }

    func removeElement(element: ElementType) {
        if let index = find(self.protocols, element) {
            self.protocols.removeAtIndex(index)
        }
    }
}

आप उस वर्ग की किसी वस्तु को तुरंत कैसे भेजेंगे?
सांप

हम्मम ... इस तरह से आप एक एकल प्रकार का उपयोग करने पर रोक लगाते हैं जो इसके अनुरूप होता है SomeProtocol-let protocolGroup: SomeClass<MyMemberClass> = SomeClass()
नैट कुक

इस तरह आप केवल श्रेणी की वस्तुओं MyMemberClassको सरणी में जोड़ सकते हैं ?
स्नोड

याlet foo = SomeClass<MyMemberClass>()
DarkDust

@snod हाँ, वह नहीं है जो आप खोज रहे हैं। मुद्दा Equatableअनुरूप है - इसके बिना आप अपने सटीक कोड का उपयोग कर सकते हैं। शायद एक बग / सुविधा अनुरोध दर्ज करें?
नैट कुक

15

स्विफ्ट में प्रोटोकॉल का एक विशेष वर्ग है जो इसे लागू करने वाले प्रकारों पर बहुरूपता प्रदान नहीं करता है। इस तरह के प्रोटोकॉल का उपयोग Selfया associatedtypeकीवर्ड उनकी परिभाषा में (और Equatableउनमें से एक है)।

कुछ मामलों में अपने संग्रह को होमोमोर्फिक बनाने के लिए एक प्रकार से मिटाए गए आवरण का उपयोग करना संभव है। नीचे एक उदाहरण है।

// This protocol doesn't provide polymorphism over the types which implement it.
protocol X: Equatable {
    var x: Int { get }
}

// We can't use such protocols as types, only as generic-constraints.
func ==<T: X>(a: T, b: T) -> Bool {
    return a.x == b.x
}

// A type-erased wrapper can help overcome this limitation in some cases.
struct AnyX {
    private let _x: () -> Int
    var x: Int { return _x() }

    init<T: X>(_ some: T) {
        _x = { some.x }
    }
}

// Usage Example

struct XY: X {
    var x: Int
    var y: Int
}

struct XZ: X {
    var x: Int
    var z: Int
}

let xy = XY(x: 1, y: 2)
let xz = XZ(x: 3, z: 4)

//let xs = [xy, xz] // error
let xs = [AnyX(xy), AnyX(xz)]
xs.forEach { print($0.x) } // 1 3

12

सीमित समाधान जो मैंने पाया वह प्रोटोकॉल को वर्ग-केवल प्रोटोकॉल के रूप में चिह्नित करना है। यह आपको '===' ऑपरेटर का उपयोग करके वस्तुओं की तुलना करने की अनुमति देगा। मैं समझता हूं कि यह संरचना आदि के लिए काम नहीं करेगा, लेकिन यह मेरे मामले में काफी अच्छा था।

protocol SomeProtocol: class {
    func bla()
}

class SomeClass {

    var protocols = [SomeProtocol]()

    func addElement(element: SomeProtocol) {
        self.protocols.append(element)
    }

    func removeElement(element: SomeProtocol) {
        for i in 0...protocols.count {
            if protocols[i] === element {
                protocols.removeAtIndex(i)
                return
            }
        }
    }

}

क्या यह डुप्लिकेट प्रविष्टियों की अनुमति नहीं देता है protocols, अगर addElementएक ही वस्तु के साथ एक से अधिक बार कहा जाता है?
टॉम हेरिंगटन

हां, स्विफ्ट में सरणियों में डुप्लिकेट प्रविष्टियां हो सकती हैं। यदि आपको लगता है कि आपके कोड में ऐसा हो सकता है, तो या तो सरणी के बजाय सेट का उपयोग करें, या सुनिश्चित करें कि सरणी में वह ऑब्जेक्ट पहले से ही नहीं है।
अल्मास

removeElement()यदि आप डुप्लिकेट से बचने की इच्छा रखते हैं, तो आप नए तत्व को जोड़ने से पहले कॉल कर सकते हैं ।
जॉर्जियोस

मेरा मतलब है कि आप अपने सरणी को हवा में कैसे नियंत्रित करते हैं, है ना? उत्तर के लिए धन्यवाद
रेमोंड हिल

9

समाधान बहुत आसान है:

protocol SomeProtocol {
    func bla()
}

class SomeClass {
    init() {}

    var protocols = [SomeProtocol]()

    func addElement<T: SomeProtocol where T: Equatable>(element: T) {
        protocols.append(element)
    }

    func removeElement<T: SomeProtocol where T: Equatable>(element: T) {
        protocols = protocols.filter {
            if let e = $0 as? T where e == element {
                return false
            }
            return true
        }
    }
}

4
आपने महत्वपूर्ण बात को याद किया: ओपी प्रोटोकॉल को वंशानुक्रम के लिए चाहता Equatableहै। इससे बहुत फर्क पड़ता है।
werediver

@werediver मुझे ऐसा नहीं लगता। वह SomeProtocolटाइप की गई सरणी के अनुरूप वस्तुओं को स्टोर करना चाहता है । Equatableसरणी से तत्वों को हटाने के लिए केवल अनुरूपता की आवश्यकता होती है। मेरा समाधान @almas समाधान का एक उन्नत संस्करण है क्योंकि इसका उपयोग किसी भी स्विफ्ट प्रकार के साथ किया जा सकता है जो Equatableप्रोटोकॉल के अनुरूप है ।
21

2

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

चूंकि किसी भी मामले में, आपको अपने objets को अलग करने के लिए कुछ विशिष्ट संपत्ति की आवश्यकता होगी, मैंने मान लिया है कि यह "नाम" है। कृपया सुनिश्चित करें कि जब आप एक नया SomeProtocol उदाहरण बनाते हैं तो आपका do.name = "foo" हो जाता है। यदि नाम सेट नहीं है, तो आप अभी भी उदाहरण बना सकते हैं, लेकिन इसे संग्रह में नहीं जोड़ा जाएगा और AddElement () "गलत" वापस आ जाएगा।

protocol SomeProtocol {
    var name:String? {get set} // Since elements need to distinguished, 
    //we will assume it is by name in this example.
    func bla()
}

class SomeClass {

    //var protocols = [SomeProtocol]() //find is not supported in 2.0, indexOf if
     // There is an Obj-C function index, that find element using custom comparator such as the one below, not available in Swift
    /*
    static func compareProtocols(one:SomeProtocol, toTheOther:SomeProtocol)->Bool {
        if (one.name == nil) {return false}
        if(toTheOther.name == nil) {return false}
        if(one.name ==  toTheOther.name!) {return true}
        return false
    }
   */

    //The best choice here is to use dictionary
    var protocols = [String:SomeProtocol]()


    func addElement(element: SomeProtocol) -> Bool {
        //self.protocols.append(element)
        if let index = element.name {
            protocols[index] = element
            return true
        }
        return false
    }

    func removeElement(element: SomeProtocol) {
        //if let index = find(self.protocols, element) { // find not suported in Swift 2.0


        if let index = element.name {
            protocols.removeValueForKey(index)
        }
    }

    func getElements() -> [SomeProtocol] {
        return Array(protocols.values)
    }
}

0

मुझे उस ब्लॉग पोस्ट पर शुद्ध-शुद्ध स्विफ्ट समाधान नहीं मिला : http://blog.inferis.org/blog/2015/05/27/swift-an-array-of-protocols/

चाल उसी के अनुरूप है, NSObjectProtocolजैसा वह परिचय देता है isEqual()। इसलिए Equatableप्रोटोकॉल का उपयोग करने और इसके डिफ़ॉल्ट उपयोग के ==बजाय आप तत्व को खोजने और इसे हटाने के लिए अपना स्वयं का कार्य लिख सकते हैं।

यहाँ आपके find(array, element) -> Int?फ़ंक्शन का कार्यान्वयन है :

protocol SomeProtocol: NSObjectProtocol {

}

func find(protocols: [SomeProtocol], element: SomeProtocol) -> Int? {
    for (index, object) in protocols.enumerated() {
        if (object.isEqual(element)) {
            return index
        }
    }

    return nil
}

नोट: इस मामले में आपकी वस्तुओं SomeProtocolको विरासत में प्राप्त होना चाहिए NSObject

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