क्या स्विफ्ट में इनिशियलाइज़ेशन के दौरान डिसेट को कॉल करना संभव है?


217

सवाल

Apple के डॉक्स निर्दिष्ट करते हैं कि:

विलसेट और डिडसेट पर्यवेक्षकों को तब नहीं बुलाया जाता है जब किसी संपत्ति को पहली बार शुरू किया जाता है। उन्हें केवल तब कहा जाता है जब संपत्ति का मूल्य एक प्रारंभिक संदर्भ के बाहर सेट किया जाता है।

क्या आरंभीकरण के दौरान इन्हें बुलाना संभव है?

क्यों?

मान लीजिए कि मेरे पास यह वर्ग है

class SomeClass {
    var someProperty: AnyObject {
        didSet {
            doStuff()
        }
    }

    init(someProperty: AnyObject) {
        self.someProperty = someProperty
        doStuff()
    }

    func doStuff() {
        // do stuff now that someProperty is set
    }
}

मैंने विधि बनाई doStuff, प्रसंस्करण कॉल को अधिक संक्षिप्त बनाने के लिए, लेकिन मैं केवल didSetफ़ंक्शन के भीतर संपत्ति को संसाधित करूंगा । क्या इनिशियलाइज़ेशन के दौरान इसे कॉल करने के लिए बाध्य करने का कोई तरीका है?

अपडेट करें

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


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

@Logan प्रश्न ने ही मेरे प्रश्न का उत्तर दिया;) धन्यवाद!
आमिर

2
यदि आप सुविधा आरंभीकरण का उपयोग करना चाहते हैं, तो आप इसके साथ संयोजन कर सकते हैं defer:convenience init(someProperty: AnyObject) { self.init() defer { self.someProperty = someProperty }
डेरेक सेइला

जवाबों:


100

अपना स्वयं का सेट-मेथड बनाएँ और इसे अपने इनिट-मेथड में उपयोग करें:

class SomeClass {
    var someProperty: AnyObject! {
        didSet {
            //do some Stuff
        }
    }

    init(someProperty: AnyObject) {
        setSomeProperty(someProperty)
    }

    func setSomeProperty(newValue:AnyObject) {
        self.someProperty = newValue
    }
}

somePropertyप्रकार के रूप में घोषित करके : AnyObject!(एक अंतर्निहित अलिखित वैकल्पिक), आप स्वयं को पूरी तरह somePropertyसे सेट किए बिना आरंभ करने की अनुमति देते हैं । जब आप कॉल setSomeProperty(someProperty)करते हैं तो आप एक बराबर कॉल कर रहे हैं self.setSomeProperty(someProperty)। आम तौर पर आप ऐसा नहीं कर पाएंगे क्योंकि स्वयं को पूरी तरह से शुरू नहीं किया गया है। चूँकि somePropertyआरंभीकरण की आवश्यकता नहीं होती है और आप स्वयं पर निर्भर एक विधि कह रहे हैं, स्विफ्ट आरंभीकरण के संदर्भ को छोड़ देता है और डिसेट चल जाएगा।


3
यह self.someProperty = init में newValue से भिन्न क्यों होगा? क्या आपको वर्किंग टेस्ट का मामला मिला है?
mmmmmm

2
पता नहीं क्यों यह काम करता है - लेकिन यह करता है। सीधे init () के भीतर नए मान को सेट करना - विधि डिडसेट को कॉल नहीं करता है () - लेकिन दिए गए विधि सेटसमप्रोपर्टी () का उपयोग करता है।
ओलिवर

50
मुझे लगता है कि मुझे पता है कि यहां क्या हो रहा है। somePropertyप्रकार के रूप में घोषित करके : AnyObject!(एक अंतर्निहित अलिखित वैकल्पिक), आप सेट selfकिए बिना पूरी तरह से आरंभ करने की अनुमति देते हैं someProperty। जब आप कॉल setSomeProperty(someProperty)करते हैं तो आप एक बराबर कॉल कर रहे हैं self.setSomeProperty(someProperty)। आम तौर पर आप ऐसा नहीं कर पाएंगे क्योंकि selfइसे पूरी तरह से शुरू नहीं किया गया है। चूँकि somePropertyआरंभीकरण की आवश्यकता नहीं है और आप एक विधि पर निर्भर कॉल कर रहे हैं self, स्विफ्ट इनिशियलाइज़ेशन संदर्भ छोड़ देता है और didSetचलेगा।
लोगन

3
ऐसा कुछ भी दिखता है जो वास्तविक init () के बाहर सेट किया जाता है, जिसे doSet कहा जाता है। शाब्दिक रूप से कुछ भी जो सेट नहीं किया गया init() { *HERE* }है, डिसेट कहा जाता है इस सवाल के लिए बढ़िया है लेकिन कुछ मूल्यों को सेट करने के लिए एक द्वितीयक फ़ंक्शन का उपयोग करने से डिसेट को कॉल किया जाता है। महान नहीं यदि आप उस सेटर फ़ंक्शन का पुन: उपयोग करना चाहते हैं।
WCByrne

4
@Mark गंभीरता से, स्विफ्ट के लिए सामान्य ज्ञान या तर्क को लागू करने की कोशिश करना सिर्फ बेतुका है। लेकिन आप upvotes पर भरोसा कर सकते हैं या इसे स्वयं आज़मा सकते हैं। मैंने अभी किया और यह काम करता है। और हाँ, इसका कोई मतलब नहीं है।
दान रोसेनस्टार्क

305

आप का उपयोग करते हैं deferएक के अंदर प्रारंभकर्ता , किसी भी वैकल्पिक गुण या आगे अद्यतन करने गैर वैकल्पिक गुण है कि आप पहले से ही प्रारंभ कर दिया है अद्यतन करने के लिए और बाद में आप किसी भी बुलाया गया है super.init()तरीकों, तो अपने willSet, didSetआदि बुलाया जाएगा। मुझे लगता है कि अलग-अलग तरीकों को लागू करने की तुलना में यह अधिक सुविधाजनक है कि आपको सही स्थानों पर कॉलिंग का ट्रैक रखना होगा।

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

public class MyNewType: NSObject {

    public var myRequiredField:Int

    public var myOptionalField:Float? {
        willSet {
            if let newValue = newValue {
                print("I'm going to change to \(newValue)")
            }
        }
        didSet {
            if let myOptionalField = self.myOptionalField {
                print("Now I'm \(myOptionalField)")
            }
        }
    }

    override public init() {
        self.myRequiredField = 1

        super.init()

        // Non-defered
        self.myOptionalField = 6.28

        // Defered
        defer {
            self.myOptionalField = 3.14
        }
    }
}

निकलेगा:

I'm going to change to 3.14
Now I'm 3.14

6
अजीब छोटी सी चाल, मुझे यह पसंद है ... स्विफ्ट की अजीब विचित्र हालांकि।
क्रिस हैटन

4
महान। आपको उपयोग करने का एक और उपयोगी मामला मिला defer। धन्यवाद।
रयान

1
हिरन का महान उपयोग! काश मैं तुम्हें एक से अधिक उत्थान दे पाता।
क्रिश्चियन स्चनोर जू

3
बहुत दिलचस्प .. मैंने पहले कभी इस तरह से इस्तेमाल किए गए डेफर को नहीं देखा है। यह वाक्पटु है और काम करता है।
अंकिस

लेकिन आपने myOptionalField को दो बार सेट किया है जो प्रदर्शन के दृष्टिकोण से महान नहीं है। क्या होगा अगर भारी गणना होती है?
याकिव कोवाल्स्की

77

ओलिवर के उत्तर की भिन्नता के रूप में, आप लाइनों को एक बंद में लपेट सकते हैं। उदाहरण के लिए:

class Classy {

    var foo: Int! { didSet { doStuff() } }

    init( foo: Int ) {
        // closure invokes didSet
        ({ self.foo = foo })()
    }

}

संपादित करें: ब्रायन वेस्टफाल का जवाब नीसर इम्हो है। उनके बारे में अच्छी बात यह है कि यह इरादे पर इशारा करता है।


2
यह चतुर है और निरर्थक तरीकों से वर्ग को प्रदूषित नहीं करता है।
22

शानदार जवाब, धन्यवाद! ध्यान देने योग्य है कि स्विफ्ट 2.2 में आपको हमेशा कोष्ठक में बंद को लपेटने की आवश्यकता होती है।
मैक्स

@Max चीयर्स, नमूना अब कोष्ठक में शामिल हैं
original_username

2
चतुर जवाब! एक नोट: यदि आपकी कक्षा किसी अन्य चीज़ का उपवर्ग है, तो आपको पहले ({self.foo = foo}) ()
kcstricks

1
@ हेलंग, मुझे यह महसूस नहीं होता कि भविष्य में किसी और चीज़ की तुलना में इसके टूटने की अधिक संभावना है, लेकिन अभी भी समस्या है कि कोड की टिप्पणी के बिना यह बहुत स्पष्ट नहीं है कि यह क्या करता है। कम से कम ब्रायन के "डिफर" उत्तर से पाठक को संकेत मिलता है कि हम आरंभीकरण के बाद कोड का प्रदर्शन करना चाहते हैं।
original_username

10

मुझे भी यही समस्या थी और यह मेरे लिए काम करता है

class SomeClass {
    var someProperty: AnyObject {
        didSet {
            doStuff()
        }
    }

    init(someProperty: AnyObject) {
        defer { self.someProperty = someProperty }
    }

    func doStuff() {
        // do stuff now that someProperty is set
    }
}

आपको यह कहाँ से मिला?
वान्या

3
यह दृष्टिकोण अब काम नहीं करता है। संकलक दो त्रुटियों को फेंकता है: - सभी संग्रहित गुणों को आरंभीकृत करने से पहले विधि कॉल '$ defer' में प्रयुक्त 'सेल्फ' - सभी संग्रहित गुणों को आरंभ किए बिना initializer से लौटें
एडवर्ड बी

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

2

यह काम करता है यदि आप एक उपवर्ग में करते हैं

class Base {

  var someProperty: AnyObject {
    didSet {
      doStuff()
    }
  }

  required init() {
    someProperty = "hello"
  }

  func doStuff() {
    print(someProperty)
  }
}

class SomeClass: Base {

  required init() {
    super.init()

    someProperty = "hello"
  }
}

let a = Base()
let b = SomeClass()

में aउदाहरण के लिए, didSetट्रिगर नहीं कर रहा है। लेकिन bउदाहरण में, didSetट्रिगर किया गया है, क्योंकि यह उपवर्ग में है। यह initialization contextवास्तव में क्या मतलब है के साथ कुछ करना है, इस मामले superclassमें उस के बारे में परवाह है


1

हालांकि यह कोई समाधान नहीं है, इसके बारे में जाने का एक वैकल्पिक तरीका एक क्लास कंस्ट्रक्टर का उपयोग करना होगा:

class SomeClass {
    var someProperty: AnyObject {
        didSet {
            // do stuff
        }
    }

    class func createInstance(someProperty: AnyObject) -> SomeClass {
        let instance = SomeClass() 
        instance.someProperty = someProperty
        return instance
    }  
}

1
इस समाधान के लिए आपको इसकी वैकल्पिकता को बदलना somePropertyहोगा क्योंकि इसने इसे आरंभीकरण के दौरान मूल्य नहीं दिया होगा।
मिक मैक्लम

1

उस विशेष मामले में जहां आप अपने सुपरक्लास में उपलब्ध संपत्ति के लिए चालान करना चाहते हैं willSetया didSetअंदर initकर सकते हैं, आप सीधे अपनी सुपर संपत्ति को सौंप सकते हैं:

override init(frame: CGRect) {
    super.init(frame: frame)
    // this will call `willSet` and `didSet`
    someProperty = super.someProperty
}

ध्यान दें कि क्लोजर के साथ चार्ल्सिज़्म समाधान हमेशा उस स्थिति में भी काम करेगा। तो मेरा समाधान सिर्फ एक विकल्प है।


-1

आप इसे obj-с तरीके से हल कर सकते हैं:

class SomeClass {
    private var _someProperty: AnyObject!
    var someProperty: AnyObject{
        get{
            return _someProperty
        }
        set{
            _someProperty = newValue
            doStuff()
        }
    }
    init(someProperty: AnyObject) {
        self.someProperty = someProperty
        doStuff()
    }

    func doStuff() {
        // do stuff now that someProperty is set
    }
}

1
हाय @yshilov, मुझे नहीं लगता कि यह वास्तव में उस समस्या को हल करता है जिसकी मैं तकनीकी रूप से उम्मीद कर रहा था, जब आप कहते हैं कि self.somePropertyआप आरंभीकरण गुंजाइश छोड़ रहे हैं। मेरा मानना ​​है कि वही काम करने से पूरा किया जा सकता है var someProperty: AnyObject! = nil { didSet { ... } }। फिर भी, कुछ इसे एक काम के रूप में सराहना कर सकते हैं, इसके अतिरिक्त धन्यवाद
लोगान

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