प्रोटोकॉल की वापसी स्व


82

मेरे पास एक प्रोटोकॉल P है जो वस्तु की एक प्रति लौटाता है:

protocol P {
    func copy() -> Self
}

और एक वर्ग C जो P को लागू करता है:

class C : P {
    func copy() -> Self {
        return C()
    }
}

हालाँकि, चाहे मैं वापसी मूल्य डालूँ क्योंकि Selfमुझे निम्नलिखित त्रुटि मिलती है:

टाइप 'स्व' को वापस करने के लिए टाइप 'C' की रिटर्न एक्सप्रेशन को बदल नहीं सकते

मैंने भी लौटने की कोशिश की C

class C : P {
    func copy() -> C  {
        return C()
    }
}

जिसके परिणामस्वरूप निम्न त्रुटि हुई:

गैर-अंतिम वर्ग 'C' में विधि 'copy' () ' Selfप्रोटोकॉल' P 'के अनुरूप होनी चाहिए ।

उस मामले के अलावा कुछ भी काम नहीं करता है जहाँ मैं उपसर्ग class Cकरता हूँ finalअर्थात करता हूँ:

final class C : P {
    func copy() -> C  {
        return C()
    }
}

हालाँकि अगर मुझे C को उप-वर्ग करना है तो कुछ भी काम नहीं करेगा। क्या इसके आसपास कोई रास्ता है?


1
"कुछ भी नहीं काम करता है" से आपका क्या मतलब है?
रोब नेपियर

संकलक शिकायत करता है जब सी या सेल्फ को रिटर्न वैल्यू के रूप में रखा classजाता है जब तक कि कोई न होfinal class
aeubanks

6
ठीक है, मैंने त्रुटियों को पुन: पेश किया है, लेकिन प्रश्न पूछते समय, आपको वास्तविक त्रुटि को शामिल करने की आवश्यकता होती है जो वापस आ जाती है। न सिर्फ "यह त्रुटियां देता है" या "यह काम नहीं करता है।"
रोब नेपियर

संकलक अपनी त्रुटियों में पूरी तरह से यहाँ सही है, बीटीडब्ल्यू। मैं सिर्फ इस बारे में सोच रहा हूं कि क्या आप वह चीज प्राप्त कर सकते हैं जो आप करने की कोशिश कर रहे हैं।
रोब नेपियर

1
लेकिन आप कॉल कर सकते हैं [[[self class] alloc] init]। इसलिए मुझे लगता है कि सवाल यह है कि क्या वर्तमान वर्ग को कॉल करने के लिए एक प्रकार-सुरक्षित तरीका है और एक इनिट विधि को कॉल करना है?
aeubanks

जवाबों:


144

समस्या यह है कि आप एक वादा कर रहे हैं कि संकलक साबित नहीं कर सकता है कि आप रखेंगे।

तो आपने यह वादा किया है: कॉलिंग copy()अपने प्रकार को लौटाएगा, पूरी तरह से प्रारंभिक।

लेकिन फिर आपने copy()इस तरह से लागू किया:

func copy() -> Self {
    return C()
}

अब मैं एक उपवर्ग हूं जो ओवरराइड नहीं करता है copy()। और मैं एक लौटाता हूं C, पूरी तरह से इनिशियलाइज्ड नहीं Self(जो मैंने वादा किया था)। तो यह अच्छा नहीं है। कैसा रहेगा:

func copy() -> Self {
    return Self()
}

ठीक है, यह संकलित नहीं होगा, लेकिन अगर यह किया, तो भी यह अच्छा नहीं होगा। उपवर्ग में कोई तुच्छ कंस्ट्रक्टर D()नहीं हो सकता है , इसलिए कानूनी भी नहीं हो सकता है। (हालांकि नीचे देखें।)

ठीक है, अच्छी तरह से कैसे के बारे में:

func copy() -> C {
    return C()
}

हां, लेकिन वह वापस नहीं आया Self। यह लौट आता है C। आप अभी भी अपना वादा नहीं निभा रहे हैं।

"लेकिन ObjC यह कर सकता है!" अच्छी तरह की। ज्यादातर क्योंकि यह परवाह नहीं करता है यदि आप अपना वादा रखते हैं जिस तरह से स्विफ्ट करता है। यदि आप copyWithZone:उपवर्ग में लागू करने में विफल रहते हैं , तो आप अपनी वस्तु को पूरी तरह से शुरू करने में विफल हो सकते हैं। संकलक आपको चेतावनी भी नहीं देगा कि आपने ऐसा किया है।

"लेकिन ObjC में सबसे अधिक सब कुछ स्विफ्ट में अनुवाद किया जा सकता है, और ObjC के पास है NSCopying।" हां यह करता है, और यहां बताया गया है कि यह कैसे परिभाषित किया गया है:

func copy() -> AnyObject!

तो आप भी ऐसा ही कर सकते हैं (इसका कोई कारण नहीं है! यहाँ):

protocol Copyable {
  func copy() -> AnyObject
}

वह कहता है, "मैं कुछ भी वादा नहीं कर रहा हूं कि आपको क्या मिलेगा।" आप यह भी कह सकते हैं:

protocol Copyable {
  func copy() -> Copyable
}

यह एक वादा है जिसे आप कर सकते हैं।

लेकिन हम थोड़ी देर के लिए सी ++ के बारे में सोच सकते हैं और याद रखें कि एक वादा है जिसे हम कर सकते हैं। हम वादा कर सकते हैं कि हम और हमारे सभी उपवर्ग प्रारंभिक प्रकार के इनिशियलाइज़र को लागू करेंगे, और स्विफ्ट लागू करेंगे (और इसलिए हम सच बता रहे हैं)

protocol Copyable {
  init(copy: Self)
}

class C : Copyable {
  required init(copy: C) {
    // Perform your copying here.
  }
}

और यह है कि आप प्रतियां कैसे प्रदर्शन करना चाहिए।

हम इसे एक कदम आगे ले जा सकते हैं, लेकिन इसका उपयोग करता है dynamicType, और मैंने यह सुनिश्चित करने के लिए बड़े पैमाने पर परीक्षण नहीं किया है कि हमेशा वही होता है जो हम चाहते हैं, लेकिन यह सही होना चाहिए:

protocol Copyable {
  func copy() -> Self
  init(copy: Self)
}

class C : Copyable {
  func copy() -> Self {
    return self.dynamicType(copy: self)
  }

  required init(copy: C) {
    // Perform your copying here.
  }
}

यहां हम वादा करते हैं कि एक इनिशलाइज़र है जो हमारे लिए प्रतियां निष्पादित करता है, और फिर हम रनटाइम पर यह निर्धारित कर सकते हैं कि किसे कॉल करना है, हमें वह विधि सिंटैक्स प्रदान करें जिसे आप खोज रहे थे।


हम्म, उन्होंने इसे बदल दिया होगा। मैं शपथ ले सकता था जो func copy() -> Cपिछले दांव में काम करता था, और यह सुसंगत था क्योंकि प्रोटोकॉल अनुरूपता विरासत में नहीं मिली थी। (अब ऐसा लगता है कि प्रोटोकॉल अनुरूपता विरासत में मिली है, और func copy() -> Cकाम नहीं करता है।)
newacct

2
अंतिम शुद्ध-स्विफ्ट समाधान उप-वर्ग के साथ काम नहीं करता है क्योंकि उन्हें init(copy: C)इसके बजाय लागू करने की आवश्यकता होती है init(copy: Self):(
तरल पदार्थ

अंतिम समाधान रिटर्न मान की गारंटी देता है, Selfलेकिन आरंभीकरणकर्ता को सभी को एक वैधानिक रूप से टाइप करने के लिए स्वीकार करना होगा, Cजो कहना है कि यह केवल AnyObjectपहले स्थान पर लौटने के लिए ज्यादा सुधार नहीं है।
चक्रित

1
स्विफ्ट 2.0 में आपको init को स्पष्ट रूप से कॉल करना होगा:self.dynamicType.init( ... )
बेन एफ्लेक

1
@ डी के अंदर सी, स्व सी या सी का एक उपवर्ग हो सकता है। वे विभिन्न प्रकार के होते हैं।
रोब नेपियर

25

स्विफ्ट 2 के साथ, हम इसके लिए प्रोटोकॉल एक्सटेंशन का उपयोग कर सकते हैं।

protocol Copyable {
    init(copy:Self)
}

extension Copyable {
    func copy() -> Self {
        return Self.init(copy: self)
    }
}

यह एक महान जवाब है और इस तरह के दृष्टिकोण की डब्ल्यूडब्ल्यूडीसी 2015 में बड़े पैमाने पर चर्चा की गई थी
gkaimakas

2
यह स्वीकृत उत्तर होना चाहिए। इसे return Self(copy: self)(स्विफ्ट 2.2 में कम से कम) के साथ सरलीकृत किया जा सकता है ।
jrmnn

16

ऐसा करने का एक और तरीका है जो आप चाहते हैं कि इसमें स्विफ्ट के जुड़े प्रकार का लाभ उठाना शामिल है। यहाँ एक सरल उदाहरण है:

public protocol Creatable {

    associatedtype ObjectType = Self

    static func create() -> ObjectType
}

class MyClass {

    // Your class stuff here
}

extension MyClass: Creatable {

    // Define the protocol function to return class type
    static func create() -> MyClass {

         // Create an instance of your class however you want
        return MyClass()
    }
}

let obj = MyClass.create()

चित्त आकर्षण करनेवाला। मुझे आश्चर्य है कि अगर वह stackoverflow.com/q/42041150/294884
फेटी

यह वही करता है जो मुझे दिलचस्पी है। धन्यवाद!
जोर्डे

10

वास्तव में, एक चाल है जो प्रोटोकॉल ( जिस्ट ) द्वारा आवश्यक होने पर आसानी से वापस Selfजाने की अनुमति देता है :

/// Cast the argument to the infered function return type.
func autocast<T>(some: Any) -> T? {
    return some as? T
}

protocol Foo {
    static func foo() -> Self
}

class Vehicle: Foo {
    class func foo() -> Self {
        return autocast(Vehicle())!
    }
}

class Tractor: Vehicle {
    override class func foo() -> Self {
        return autocast(Tractor())!
    }
}

func typeName(some: Any) -> String {
    return (some is Any.Type) ? "\(some)" : "\(some.dynamicType)"
}

let vehicle = Vehicle.foo()
let tractor = Tractor.foo()

print(typeName(vehicle)) // Vehicle
print(typeName(tractor)) // Tractor

1
वाह। संकलन करता है। यह मुश्किल है, क्योंकि संकलक आपको बस नहीं देगाreturn Vehicle() as! Self
SimpleGy

वह मनमौजी है। वाह। क्या मैं यहाँ पूछ रहा हूँ वास्तव में इस पर एक भिन्नता है ?? stackoverflow.com/q/42041150/294884
फेटी

@JoeBlow मुझे डर है कि यह नहीं है। मैं कहूंगा कि अपने दिमाग को सुरक्षित रखने के लिए हमें रिटर्न प्रकार (अर्थात "A या B" नहीं, बल्कि सिर्फ "A" जानना चाहिए, अन्यथा हमें बहुरूपता + वंशानुक्रम + कार्य अतिभार (कम से कम) के बारे में सोचना चाहिए।
wverver

कंपाइलर ट्रिकिंग। चूंकि ओवरराइडिंग को foo()लागू नहीं किया गया है, Vehicleबिना foo()कस्टम कार्यान्वयन के प्रत्येक वंश में स्पष्ट दुर्घटना का उत्पादन होगा autocast()। उदाहरण के लिए class SuperCar: Vehicle { } let superCar = SuperCar.foo() :। उदाहरण के लिए नीचे Vehicleनहीं SuperCarउतारा जा सकता है - इसलिए 'ऑटोकैस्ट ()' में शून्य से निकले बल को दुर्घटना की ओर ले जाता है।
freennnn

1
@freennnn कोड को निम्न में बदलने से दुर्घटना नहीं होती है जब एक उपवर्ग ओवरराइड नहीं करता है foo()। आवश्यकता केवल Fooइस बात की है कि नीचे दिखाए गए अनुसार काम करने के लिए कक्षा के पास आवश्यक इनिशियलाइज़र होना चाहिए। class Vehicle: Foo { public required init() { // Some init code here } class func foo() -> Self { return autocast(self.init())! // return autocast(Vehicle())! } } class Tractor: Vehicle { //Override is not necessary /*override class func foo() -> Self { return autocast(Tractor())! }*/ }
shawnynicole

2

रोब के सुझाव के बाद, इसे संबद्ध प्रकारों के साथ अधिक सामान्य बनाया जा सकता है । मैंने दृष्टिकोण के लाभों को प्रदर्शित करने के लिए उदाहरण को थोड़ा बदल दिया है।

protocol Copyable: NSCopying {
    associatedtype Prototype
    init(copy: Prototype)
    init(deepCopy: Prototype)
}
class C : Copyable {
    typealias Prototype = C // <-- requires adding this line to classes
    required init(copy: Prototype) {
        // Perform your copying here.
    }
    required init(deepCopy: Prototype) {
        // Perform your deep copying here.
    }
    @objc func copyWithZone(zone: NSZone) -> AnyObject {
        return Prototype(copy: self)
    }
}

1

मुझे एक समान समस्या थी और कुछ ऐसा मिला जो उपयोगी हो सकता है इसलिए मैं इसे भविष्य में संदर्भ के लिए साझा करूंगा क्योंकि यह पहली जगह है जो मैंने हल खोजते समय पाया।

जैसा कि ऊपर कहा गया है, समस्या प्रतिलिपि () फ़ंक्शन के लिए वापसी प्रकार की अस्पष्टता है। इसे कॉपी () -> C और कॉपी () -> P फ़ंक्शन को अलग करके बहुत स्पष्ट रूप से चित्रित किया जा सकता है:

तो, मान लें कि आप प्रोटोकॉल और वर्ग को निम्नानुसार परिभाषित करते हैं:

protocol P
{
   func copy() -> P
}

class C:P  
{        
   func doCopy() -> C { return C() }       
   func copy() -> C   { return doCopy() }
   func copy() -> P   { return doCopy() }       
}

यह रिटर्न परिणाम का प्रकार स्पष्ट होने पर अपेक्षित परिणाम संकलित और तैयार करता है। किसी भी समय संकलक को यह तय करना होगा कि रिटर्न प्रकार क्या होना चाहिए (अपने दम पर), यह स्थिति को अस्पष्ट और सभी ठोस वर्गों के लिए विफल हो जाएगा जो पी प्रोटोकॉल को लागू करते हैं।

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

var aC:C = C()   // aC is of type C
var aP:P = aC    // aP is of type P (contains an instance of C)

var bC:C         // this to test assignment to a C type variable
var bP:P         //     "       "         "      P     "    "

bC = aC.copy()         // OK copy()->C is used

bP = aC.copy()         // Ambiguous. 
                       // compiler could use either functions
bP = (aC as P).copy()  // but this resolves the ambiguity.

bC = aP.copy()         // Fails, obvious type incompatibility
bP = aP.copy()         // OK copy()->P is used

अंत में, यह उन स्थितियों में काम करेगा जहां आप या तो हैं, बेस क्लास की कॉपी () फ़ंक्शन का उपयोग नहीं कर रहे हैं या आपके पास हमेशा स्पष्ट प्रकार का संदर्भ है।

मैंने पाया कि हर जगह बिना कोड के लिए बनाए गए ठोस वर्ग के समान फ़ंक्शन नाम का उपयोग करना, इसलिए मैंने प्रोटोकॉल की प्रतिलिपि () फ़ंक्शन के लिए एक अलग नाम का उपयोग करके समाप्त कर दिया।

अंतिम परिणाम अधिक है:

protocol P
{
   func copyAsP() -> P
}

class C:P  
{
   func copy() -> C 
   { 
      // there usually is a lot more code around here... 
      return C() 
   }
   func copyAsP() -> P { return copy() }       
}

बेशक मेरा संदर्भ और कार्य पूरी तरह से अलग हैं लेकिन सवाल की भावना में, मैंने यथासंभव दिए गए उदाहरण के करीब रहने की कोशिश की।


1

स्विफ्ट 5.1 अब स्व के लिए एक मजबूर कलाकार को अनुमति दें, as! Self

  1> protocol P { 
  2.     func id() -> Self 
  3. } 
  9> class D : P { 
 10.     func id() -> Self { 
 11.         return D()
 12.     } 
 13. } 
error: repl.swift:11:16: error: cannot convert return expression of type 'D' to return type 'Self'
        return D()
               ^~~
                   as! Self


  9> class D : P { 
 10.     func id() -> Self { 
 11.         return D() as! Self
 12.     } 
 13. } //works

0

बस मेरी टोपी को रिंग में फेंक दिया। हमें एक ऐसे प्रोटोकॉल की आवश्यकता थी, जिस प्रोटोकॉल पर लागू किया गया था उस प्रकार का एक वैकल्पिक लौटाया जाए। हम यह भी चाहते थे कि ओवरराइड स्पष्ट रूप से टाइप करे, न कि केवल सेल्फ।

चाल वापसी के रूप में 'स्व' का उपयोग करने के बजाय है, आप इसके बजाय एक संबद्ध प्रकार को परिभाषित करते हैं जिसे आप स्व के बराबर सेट करते हैं, फिर उस संबद्ध प्रकार का उपयोग करें।

यहां जानिए सेल्फ का इस्तेमाल करने का पुराना तरीका ...

protocol Mappable{
    static func map() -> Self?
}

// Generated from Fix-it
extension SomeSpecificClass : Mappable{
    static func map() -> Self? {
        ...
    }
}

यहां संबंधित प्रकार का उपयोग करके नया तरीका बताया गया है। ध्यान दें कि रिटर्न प्रकार अब स्पष्ट है, 'स्व' नहीं।

protocol Mappable{
    associatedtype ExplicitSelf = Self
    static func map() -> ExplicitSelf?
}

// Generated from Fix-it
extension SomeSpecificClass : Mappable{
    static func map() -> SomeSpecificClass? {
        ...
    }
}

0

associatedtypeरास्ते के साथ उत्तरों को जोड़ने के लिए , मैं प्रोटोकॉल एक्सटेंशन के डिफ़ॉल्ट कार्यान्वयन के लिए इंस्टेंस के निर्माण को स्थानांतरित करने का सुझाव देता हूं। इस तरह अनुरूप वर्गों को इसे लागू नहीं करना होगा, इस प्रकार हमें कोड डुप्लीकेशन से अलग किया जाएगा:

protocol Initializable {
    init()
}

protocol Creatable: Initializable {
    associatedtype Object: Initializable = Self
    static func newInstance() -> Object
}

extension Creatable {
    static func newInstance() -> Object {
        return Object()
    }
}

class MyClass: Creatable {
    required init() {}
}

class MyOtherClass: Creatable {
    required init() {}
}

// Any class (struct, etc.) conforming to Creatable
// can create new instances without having to implement newInstance() 
let instance1 = MyClass.newInstance()
let instance2 = MyOtherClass.newInstance()
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.