स्विफ्ट एक्सटेंशन में ओवरराइडिंग के तरीके


133

मैं केवल अपनी कक्षा की परिभाषाओं में आवश्यकताएं (संग्रहीत गुण, इनिशियलाइज़र) रखता हूं और बाकी सभी को अपने आप में स्थानांतरित करता हूं extension, एक extensionतार्किक ब्लॉक की // MARK:तरह।

उदाहरण के लिए एक UIView उपवर्ग के लिए, मैं लेआउट-संबंधित सामान के लिए एक एक्सटेंशन के साथ, एक को आगे बढ़ने और घटनाओं को संभालने और संभालने के लिए समाप्त करूंगा। इन एक्सटेंशनों में, मुझे अनिवार्य रूप से कुछ UIKit विधियों को ओवरराइड करना है, जैसे layoutSubviews। मैंने कभी भी इस दृष्टिकोण के साथ किसी भी मुद्दे पर ध्यान नहीं दिया - आज तक।

उदाहरण के लिए इस वर्ग पदानुक्रम को लें:

public class C: NSObject {
    public func method() { print("C") }
}

public class B: C {
}
extension B {
    override public func method() { print("B") }
}

public class A: B {
}
extension A {
    override public func method() { print("A") }
}

(A() as A).method()
(A() as B).method()
(A() as C).method()

आउटपुट है A B C। इससे मुझे कोई मतलब नहीं है। मैंने प्रोटोकॉल एक्सटेंशन के बारे में स्टेटिकली डिस्पैच होने के बारे में पढ़ा, लेकिन यह प्रोटोकॉल नहीं है। यह एक नियमित कक्षा है, और मुझे उम्मीद है कि विधि कॉल को गतिशील रूप से रनटाइम पर भेजा जाएगा। स्पष्ट रूप से कॉल को Cकम से कम गतिशील रूप से भेजा जाना चाहिए और उत्पादन करना चाहिए C?

यदि मैं विरासत को हटा देता हूं NSObjectऔर Cएक मूल वर्ग बनाता हूं , तो कंपाइलर यह कहते हुए शिकायत करता है declarations in extensions cannot override yet, जिसे मैंने पहले से ही पढ़ा है। लेकिन NSObjectएक जड़ वर्ग के रूप में चीजों को कैसे बदलता है?

अपने वर्ग घोषणा में दोनों ओवरराइड चलती का उत्पादन A A Aकी उम्मीद के रूप में, केवल आगे बढ़ B's पैदा करता है A B B, केवल आगे बढ़ Aरहा है पैदा करता है C B C, जिनमें से आखिरी मेरे लिए बिल्कुल नहीं समझ में आता है: नहीं एक भी स्थिर लिखे गए Aपैदा करता है Aआउटपुट किसी भी अधिक!

dynamicकीवर्ड को परिभाषा या एक ओवरराइड में जोड़ने से मुझे कक्षा पदानुक्रम में उस बिंदु से 'वांछित व्यवहार' देना प्रतीत होता है ...

आइए हमारे उदाहरण को कुछ कम निर्मित निर्माण में बदलें, जिसने वास्तव में मुझे इस प्रश्न का उत्तर दिया:

public class B: UIView {
}
extension B {
    override public func layoutSubviews() { print("B") }
}

public class A: B {
}
extension A {
    override public func layoutSubviews() { print("A") }
}


(A() as A).layoutSubviews()
(A() as B).layoutSubviews()
(A() as UIView).layoutSubviews()

अब हम मिल गए A B A। यहाँ मैं किसी भी तरह से UIView के लेआउटसुबानों को गतिशील नहीं बना सकता।

दोनों को अपनी कक्षा की घोषणा में आगे ले जाने से हमें A A Aफिर से केवल A या केवल B की प्राप्ति होती है A B Adynamicफिर से मेरी समस्याओं को हल करता है।

सिद्धांत रूप में मैं उन dynamicसभी को जोड़ सकता overrideहूं जो मैं कभी भी करता हूं लेकिन मुझे ऐसा लगता है कि मैं यहां कुछ और गलत कर रहा हूं।

क्या extensionमेरे जैसे ग्रुप कोडिंग के लिए एस का उपयोग करना वास्तव में गलत है?


एक्सटेंशन का उपयोग करना इस तरह से स्विफ्ट के लिए कन्वेंशन है। यहां तक ​​कि Apple इसे मानक पुस्तकालय में भी करता है।
अलेक्जेंडर -


1
@AMomchilov जिस दस्तावेज़ को आपने प्रोटोकॉल के बारे में बातचीत से जोड़ा है, क्या मैं कुछ याद कर रहा हूं?
क्रिश्चियन स्चनोर जूल

मुझे संदेह है कि यह एक ही तंत्र है जो दोनों के लिए काम करता है
अलेक्जेंडर - पुनः स्थापित मोनिका

3
उप-एक्सटेंशन एक्सटेंशन में ओवरराइड की गई विधियों में स्विफ्ट प्रेषण की नकल करने की अपील करता है । मैट का जवाब है कि यह एक बग है (और वह डॉक्स को उस समर्थन के लिए उद्धृत करता है)।
jscs

जवाबों:


229

एक्सटेंशन ओवरराइड नहीं कर सकते / नहीं करना चाहिए।

Apple की स्विफ्ट गाइड में प्रलेखित के रूप में एक्सटेंशन में कार्यक्षमता (गुणों या विधियों की तरह) को ओवरराइड करना संभव नहीं है।

एक्सटेंशन एक प्रकार से नई कार्यक्षमता जोड़ सकते हैं, लेकिन वे मौजूदा कार्यक्षमता को ओवरराइड नहीं कर सकते।

स्विफ्ट डेवलपर गाइड

कंपाइलर आपको ऑब्जेक्ट-सी के साथ संगतता के लिए एक्सटेंशन में ओवरराइड करने की अनुमति दे रहा है। लेकिन यह वास्तव में भाषा के निर्देश का उल्लंघन है।

😊इसने मुझे इसहाक असिमोव के " थ्री लॉज ऑफ रोबोटिक्स " की याद दिला दी

एक्सटेंशन्स ( सिंटैक्टिक शुगर ) स्वतंत्र तरीकों को परिभाषित करते हैं जो अपने स्वयं के तर्क प्राप्त करते हैं। फ़ंक्शन के लिए कहा जाने वाला फ़ंक्शन उस layoutSubviewsसंदर्भ पर निर्भर करता है जब कोड संकलित होने पर कंपाइलर जानता है। UIView को UIResponder से विरासत में मिला है जो NSObject से विरासत में मिला है इसलिए एक्सटेंशन में ओवरराइड की अनुमति है लेकिन ऐसा नहीं होना चाहिए

इसलिए समूहीकरण में कुछ भी गलत नहीं है, लेकिन आपको विस्तार में नहीं कक्षा में ओवरराइड करना चाहिए।

डायरेक्टिव नोट्स

आप केवल overrideसुपरक्लास पद्धति हो सकते हैं अर्थात load() initialize()यदि उप-उद्देश्य संगत है तो एक उप-वर्ग के विस्तार में।

इसलिए हम इस पर एक नज़र डाल सकते हैं कि यह आपको उपयोग करने के लिए संकलित करने की अनुमति क्यों दे रहा है layoutSubviews

सभी स्विफ्ट ऐप्स, ऑब्जेक्ट-सी रनटाइम के अंदर निष्पादित करते हैं, सिवाय शुद्ध स्विफ्ट-केवल फ़्रेमवर्क का उपयोग करते हुए जो स्विफ्ट-केवल रनटाइम की अनुमति देते हैं।

जैसा कि हमने पाया कि ऑब्जेक्टिव-सी रनटाइम आम तौर पर दो क्लास के मुख्य तरीकों को कॉल करता है load()और initialize()स्वचालित रूप से आपके ऐप की प्रक्रियाओं में कक्षाएं शुरू करते समय।

dynamicसंशोधक के संबंध में

से Apple डेवलपर लाइब्रेरी (archive.org)

आप dynamicसंशोधक का उपयोग इस बात की आवश्यकता के लिए कर सकते हैं कि सदस्यों तक पहुंच गतिशील रूप से ऑब्जेक्टिव-सी रनटाइम के माध्यम से भेजी जाए।

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

तो dynamicआपके layoutSubviews-> पर लागू किया जा सकता है UIView Classक्योंकि यह उद्देश्य-सी द्वारा दर्शाया गया है और उस सदस्य तक पहुंच हमेशा उद्देश्य-सी रनटाइम का उपयोग करके की जाती है।

यही कारण है कि संकलक आपको उपयोग करने की अनुमति देता है overrideऔर dynamic


6
विस्तार केवल कक्षा में परिभाषित तरीकों को ओवरराइड नहीं कर सकता है। यह मूल वर्ग में परिभाषित विधियों को ओवरराइड कर सकता है।
RJE

-Swift3- ठीक है, यह अजीब है, क्योंकि आप ओवरराइड भी कर सकते हैं (और यहां ओवरराइड करके मेरा मतलब है कि स्विज़लिंग जैसा कुछ) आपके द्वारा शामिल चौखटे से तरीके। यहां तक ​​कि अगर उन रूपरेखाओं को शुद्ध स्विफ्ट में लिखा गया है .... शायद फ्रेमवर्क भी objc के लिए बाध्य हैं और इसीलिए b
farzadshbfn

@tymac मुझे नहीं मिला। यदि ऑब्जेक्टिव-सी रनटाइम को ऑब्जेक्टिव-सी कम्पैटिबिलिटी के लिए कुछ चाहिए, तो स्विफ्ट कंपाइलर अभी भी एक्सटेंशन में ओवरराइडिंग की अनुमति क्यों देता है? सिंटैक्स त्रुटि के रूप में स्विफ्ट एक्सटेंशन में ओवरराइडिंग को कैसे चिह्नित किया जा सकता है?
अलेक्जेंडर वासिनिन

1
इसलिए निराशा होती है, इसलिए मूल रूप से जब आप किसी प्रोजेक्ट में पहले से ही एक कोड के साथ एक फ्रेमवर्क बनाना चाहते हैं, तो आपको सब कुछ उप-लिंक और नाम बदलना होगा ...
thibaut noah

3
@AuRis क्या आपके पास कोई संदर्भ है?
रिकार्डोपेरेरा

18

स्विफ्ट के लक्ष्यों में से एक स्थिर प्रेषण, या बल्कि गतिशील प्रेषण की कमी है। ओब्ज-सी हालांकि एक बहुत ही गतिशील भाषा है। जिस स्थिति को आप देख रहे हैं, वह 2 भाषाओं के बीच की कड़ी और उनके काम करने के तरीके से पैदा होती है। यह वास्तव में संकलन नहीं होना चाहिए।

एक्सटेंशन के बारे में मुख्य बिंदुओं में से एक यह है कि वे विस्तार के लिए हैं, न कि प्रतिस्थापित / ओवरराइड करने के लिए। यह नाम और प्रलेखन दोनों से स्पष्ट है कि यह इरादा है। वास्तव में यदि आप अपने कोड से Obj-C का लिंक निकालते हैं ( NSObjectसुपरक्लास को हटा दें ) यह संकलन नहीं करेगा।

इसलिए, संकलक यह तय करने की कोशिश कर रहा है कि यह सांख्यिकीय रूप से क्या प्रेषण कर सकता है और इसे गतिशील रूप से प्रेषण करने के लिए क्या है, और यह आपके कोड में ओज-सी लिंक के कारण अंतराल के माध्यम से गिर रहा है। कारण dynamic'काम करता है' क्योंकि यह ओज-सी को हर चीज से जोड़ने के लिए मजबूर करता है इसलिए यह सब हमेशा गतिशील है।

इसलिए, समूहीकरण के लिए एक्सटेंशन का उपयोग करना गलत नहीं है, यह बहुत अच्छा है, लेकिन एक्सटेंशन में ओवरराइड करना गलत है। कोई भी ओवरराइड मुख्य वर्ग में ही होना चाहिए, और विस्तार बिंदुओं पर कॉल करना चाहिए।


यह चर पर भी लागू होता है? उदाहरण के लिए, यदि आप ओवरराइड करना चाहते supportedInterfaceOrientationsमें UINavigationController(विभिन्न झुकाव के विभिन्न दृश्य दिखाने का प्रयोजनों के लिए), आप एक कस्टम नहीं वर्ग एक विस्तार इस्तेमाल करना चाहिए? बहुत से जवाबों को ओवरराइड करने के लिए एक एक्सटेंशन का उपयोग करने का सुझाव है supportedInterfaceOrientationsलेकिन स्पष्टीकरण को पसंद करेंगे। धन्यवाद!
Crashalot

10

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

यदि आप प्रत्येक उपवर्ग को एक अलग स्विफ्ट स्रोत फ़ाइल में परिभाषित करना सुनिश्चित करते हैं, तो आप इसी कार्यान्वयन को विस्तार से सफाई से व्यवस्थित रखते हुए ओवरराइड्स के लिए गणना किए गए चर का उपयोग कर सकते हैं। यह स्विफ्ट के "नियमों" को दरकिनार कर देगा और आपकी कक्षा के एपीआई / हस्ताक्षर को बड़े करीने से एक स्थान पर व्यवस्थित कर देगा:

// ---------- BaseClass.swift -------------

public class BaseClass
{
    public var method1:(Int) -> String { return doMethod1 }

    public init() {}
}

// the extension could also be in a separate file  
extension BaseClass
{    
    private func doMethod1(param:Int) -> String { return "BaseClass \(param)" }
}

...

// ---------- ClassA.swift ----------

public class A:BaseClass
{
   override public var method1:(Int) -> String { return doMethod1 }
}

// this extension can be in a separate file but not in the same
// file as the BaseClass extension that defines its doMethod1 implementation
extension A
{
   private func doMethod1(param:Int) -> String 
   { 
      return "A \(param) added to \(super.method1(param))" 
   }
}

...

// ---------- ClassB.swift ----------
public class B:A
{
   override public var method1:(Int) -> String { return doMethod1 }
}

extension B
{
   private func doMethod1(param:Int) -> String 
   { 
      return "B \(param) added to \(super.method1(param))" 
   }
}

प्रत्येक वर्ग का विस्तार कार्यान्वयन के लिए समान विधि नामों का उपयोग करने में सक्षम होता है क्योंकि वे निजी होते हैं और एक-दूसरे को दिखाई नहीं देते हैं (जब तक वे अलग-अलग फ़ाइलों में होते हैं)।

जैसा कि आप देख सकते हैं वंशानुक्रम (चर नाम का उपयोग करके) सुपर .variablename का उपयोग करके ठीक से काम करता है

BaseClass().method1(123)         --> "BaseClass 123"
A().method1(123)                 --> "A 123 added to BaseClass 123"
B().method1(123)                 --> "B 123 added to A 123 added to BaseClass 123"
(B() as A).method1(123)          --> "B 123 added to A 123 added to BaseClass 123"
(B() as BaseClass).method1(123)  --> "B 123 added to A 123 added to BaseClass 123"

2
मुझे लगता है कि मेरे अपने तरीकों के लिए काम करेगा, लेकिन मेरी कक्षाओं में सिस्टम फ्रेमवर्क के तरीकों को ओवरराइड करते समय नहीं।
क्रिश्चियन स्चनोर

इसने मुझे एक संपत्ति आवरण सशर्त प्रोटोकॉल विस्तार के लिए सही रास्ता दिखाया। धन्यवाद!
क्रिस प्रिंस

1

इसका जवाब यह ओपी के उद्देश्य से नहीं है, इस तथ्य के अलावा कि मुझे उनके बयान से जवाब देने के लिए प्रेरित महसूस हुआ, "मैं केवल अपनी कक्षा की परिभाषाओं में आवश्यकताओं (संग्रहीत गुण, इनिशियलाइज़र) को रखता हूं और बाकी सब कुछ अपने स्वयं के विस्तार में स्थानांतरित करता हूं। .. "। मैं मुख्य रूप से एक C # प्रोग्रामर हूं, और C # में इस उद्देश्य के लिए आंशिक कक्षाओं का उपयोग कर सकता हूं। उदाहरण के लिए, Visual Studio एक आंशिक वर्ग का उपयोग करके UI से संबंधित सामान को एक अलग स्रोत फ़ाइल में रखता है, और आपकी मुख्य स्रोत फ़ाइल को अप्रस्तुत छोड़ देता है ताकि आपके पास वह व्याकुलता न हो।

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

वैसे भी, एक लंबे समय से घुमावदार परिचय को कम करने के लिए, मैं इस समस्या में एक ऐसी स्थिति में भाग गया, जहां मैं स्विफ्ट कक्षाओं के लिए मुख्य स्रोत फ़ाइलों में से कुछ बॉयलरप्लेट / बैगेज विधियों को स्थानांतरित करना चाहता था जो कि मेरा C # -to-Swift प्रोग्राम उत्पन्न कर रहा था। एक्सटेंशन पर जाने के बाद इन तरीकों के लिए अनुमति नहीं ओवरराइड की समस्या में चलने के बाद, मैंने निम्नलिखित सरल-दिमाग वाले वर्कअराउंड को लागू करना समाप्त कर दिया। मुख्य स्विफ्ट स्रोत फ़ाइलों में अभी भी कुछ छोटे स्टब विधियाँ हैं जो एक्सटेंशन फ़ाइलों में वास्तविक तरीकों को कहते हैं, और इन विस्तार विधियों को ओवरराइड समस्या से बचने के लिए अद्वितीय नाम दिए गए हैं।

public protocol PCopierSerializable {

   static func getFieldTable(mCopier : MCopier) -> FieldTable
   static func createObject(initTable : [Int : Any?]) -> Any
   func doSerialization(mCopier : MCopier)
}

public class SimpleClass : PCopierSerializable {

   public var aMember : Int32

   public init(
               aMember : Int32
              ) {
      self.aMember = aMember
   }

   public class func getFieldTable(mCopier : MCopier) -> FieldTable {
      return getFieldTable_SimpleClass(mCopier: mCopier)
   }

   public class func createObject(initTable : [Int : Any?]) -> Any {
      return createObject_SimpleClass(initTable: initTable)
   }

   public func doSerialization(mCopier : MCopier) {
      doSerialization_SimpleClass(mCopier: mCopier)
   }
}

extension SimpleClass {

   class func getFieldTable_SimpleClass(mCopier : MCopier) -> FieldTable {
      var fieldTable : FieldTable = [ : ]
      fieldTable[376442881] = { () in try mCopier.getInt32A() }  // aMember
      return fieldTable
   }

   class func createObject_SimpleClass(initTable : [Int : Any?]) -> Any {
      return SimpleClass(
                aMember: initTable[376442881] as! Int32
               )
   }

   func doSerialization_SimpleClass(mCopier : MCopier) {
      mCopier.writeBinaryObjectHeader(367620, 1)
      mCopier.serializeProperty(376442881, .eInt32, { () in mCopier.putInt32(aMember) } )
   }
}

public class DerivedClass : SimpleClass {

   public var aNewMember : Int32

   public init(
               aNewMember : Int32,
               aMember : Int32
              ) {
      self.aNewMember = aNewMember
      super.init(
                 aMember: aMember
                )
   }

   public class override func getFieldTable(mCopier : MCopier) -> FieldTable {
      return getFieldTable_DerivedClass(mCopier: mCopier)
   }

   public class override func createObject(initTable : [Int : Any?]) -> Any {
      return createObject_DerivedClass(initTable: initTable)
   }

   public override func doSerialization(mCopier : MCopier) {
      doSerialization_DerivedClass(mCopier: mCopier)
   }
}

extension DerivedClass {

   class func getFieldTable_DerivedClass(mCopier : MCopier) -> FieldTable {
      var fieldTable : FieldTable = [ : ]
      fieldTable[376443905] = { () in try mCopier.getInt32A() }  // aNewMember
      fieldTable[376442881] = { () in try mCopier.getInt32A() }  // aMember
      return fieldTable
   }

   class func createObject_DerivedClass(initTable : [Int : Any?]) -> Any {
      return DerivedClass(
                aNewMember: initTable[376443905] as! Int32,
                aMember: initTable[376442881] as! Int32
               )
   }

   func doSerialization_DerivedClass(mCopier : MCopier) {
      mCopier.writeBinaryObjectHeader(367621, 2)
      mCopier.serializeProperty(376443905, .eInt32, { () in mCopier.putInt32(aNewMember) } )
      mCopier.serializeProperty(376442881, .eInt32, { () in mCopier.putInt32(aMember) } )
   }
}

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


1

एक्सटेंशन में फ़ंक्शन को ओवरराइड करने के लिए POP (प्रोटोकॉल-ओरिएंटेड प्रोग्रामिंग) का उपयोग करें।

protocol AProtocol {
    func aFunction()
}

extension AProtocol {
    func aFunction() {
        print("empty")
    }
}

class AClass: AProtocol {

}

extension AClass {
    func aFunction() {
        print("not empty")
    }
}

let cls = AClass()
cls.aFunction()

1
यह मानता है कि प्रोग्रामर AClass की मूल परिभाषा के नियंत्रण में है जैसे कि यह AProtocol पर भरोसा कर सकता है। ऐसी स्थिति में जहां कोई एसीलस में कार्यक्षमता को ओवरराइड करना चाहता है, यह आमतौर पर ऐसा नहीं होता है (यानी, AClass संभवतः एक मानक पुस्तकालय वर्ग होगा जो Apple द्वारा प्रदान किया गया है)।
जोनाथन लियोनार्ड

ध्यान दें, आप (कुछ मामलों में) एक विस्तार या उपवर्ग में प्रोटोकॉल लागू कर सकते हैं यदि आप कक्षा की मूल परिभाषा को संशोधित नहीं कर सकते हैं या नहीं कर सकते हैं।
शिम
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.