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

आइए एक साधारण उदाहरण पर विचार करें। हमारे पास दो वर्ग हैं, Foo और Bar, जहां Bar का एक उदाहरण एक संकेत bar_signal का उत्सर्जन करता है, जिसे Foo के उदाहरण द्वारा नियंत्रित किया जाता है। हालांकि, यह सिग्नल कमजोर रूप से जुड़ा हुआ है, जिससे Foo इंस्टेंस को हटाया जा सकता है, कनेक्शन अभी भी ठीक क्यों है:

class Bar : Object {
    public signal void bar_signal();
}

class Foo : Object {
    string tag;
    ulong hid;
    Bar bar;

    public Foo( Bar bar, string tag ) {
        this.tag = tag;
        this.bar = bar;
        weak Foo weak_this = this;
        hid = bar.bar_signal.connect( weak_this.handle );
    }

    ~Foo() {
        stdout.printf( "foo finalized\n" );
    }

    private void handle() {
        stdout.printf( "handler: %s\n", tag );
    }
}

public static void main( string[] args ) {
    Bar bar = new Bar();
    {
        Foo foo = new Foo( bar, "x" );
        bar.bar_signal(); // writes "handler: x"
    }                     // foo destroyed at end of block
    bar.bar_signal();     // writes nothing
}

के अनुसार ">वह टिप्पणी, Foo इंस्टेंस नष्ट हो जाने के बाद, हैंडलर संदर्भ bar_signal में एक लटकने वाले सूचक के रूप में बना रहता है, क्योंकि हम इसे अभी तक डिस्कनेक्ट नहीं कर रहे हैं।

मैंने a adding जोड़ने पर विचार किया है

bar.bar_signal.disconnect( handle );

Foo का विनाशक, जो ठीक काम करता है।

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

प्रश्न: हम संबंधित connect कॉल के रिटर्न वैल्यू का उपयोग करके हैंडलर को सिग्नल से डिस्कनेक्ट भी कर सकते हैं। उदाहरण के लिए connect कॉल के ठीक बाद किए जाने पर यह बहुत अच्छा काम करता है। लेकिन यह विनाशक में विफल क्यों होता है?

~Foo() {
    stdout.printf( "foo finalized\n" );
    bar.disconnect( hid ); // <--!!
}

यह निम्नलिखित संकेत के साथ विफल रहता है:

जीएलआईबी-गोब्जेक्ट-चेतावनी **: उदाहरण '0x8c8820' में आईडी '1' वाला कोई हैंडलर नहीं है


अपडेट

अपडेट 1: दिलचस्प बात यह है कि bar.disconnect(hid ) इनवोकेशन काम करता है, अगर यह Foo के डिस्पोजल के भीतर से किया जाता है विधि। GObject की स्मृति प्रबंधन, निपटान वस्तु को अंतिम रूप देने से ठीक पहले कहा जाता है (जो मुझे लगता है कि वैला में विनाशक से मेल खाती है) और इसका इरादा अन्य के सभी संदर्भों को काटना है ऑब्जेक्ट्स। विशेष रूप से, जैसा कि @AlThomas द्वारा बताया गया है, "सिग्नल हैंडलर [...] सिग्नल हैंडलर उपयोगकर्ता डेटा नष्ट होने पर स्वचालित रूप से डिस्कनेक्ट नहीं होते हैं।" तो हम डिस्कनेक्ट क्यों कर सकते हैं निपटान से सिग्नल हैंडलर, लेकिन तब नहीं जब Foo को अंतिम रूप दिया जाता है, जो कि GObject के दस्तावेज़ वास्तव में अनुशंसा करता है?

एकमात्र स्पष्टीकरण, जो मेरे दिमाग में आता है, वह यह है कि: वैला/जीएलआईबी do से पहले मुझे जो बताया गया है, उसके विपरीत ऑब्जेक्ट को निपटाने और अंतिम रूप देने के बीच सिग्नल हैंडलर को अपने आप डिस्कनेक्ट करें। हालांकि, यह मेरे लिए काफी असंभव लगता है, इसलिए मैं वास्तव में और स्पष्टीकरण की सराहना करता हूं।

अपडेट 2: पता चला, उपरोक्त गलत नहीं था :)

2
theV0ID 19 अगस्त 2016, 11:18

1 उत्तर

सबसे बढ़िया उत्तर

एक सामान्य नोट के रूप में, Vala के संकेत GLib के GObject संकेतों का उपयोग करके पर्यवेक्षक पैटर्न का कार्यान्वयन हैं। तो जानकारी का एक अच्छा स्रोत जीएलआईबी का अपना दस्तावेज है। उदाहरण के लिए Signals और सिग्नल कैसे बनाएं और उनका उपयोग कैसे करें। बाद वाला बताते हुए:

GType में सिग्नल सिस्टम काफी जटिल और लचीला है: इसके उपयोगकर्ताओं के लिए रनटाइम पर किसी भी सिग्नल को किसी भी संख्या में कॉलबैक (किसी भी भाषा में लागू किया गया है जिसके लिए बाध्यकारी मौजूद है) को कनेक्ट करना और किसी भी राज्य में किसी भी सिग्नल के उत्सर्जन को रोकना संभव है। सिग्नल उत्सर्जन प्रक्रिया

एक जटिल विषय के लिए अच्छे दस्तावेज़ीकरण की आवश्यकता होती है और आपके वाला विशिष्ट प्रश्न इस विषय पर ज्ञान के शरीर का निर्माण करने में मदद करते हैं।

आपके प्रश्न के लिए GLib दस्तावेज़ का सबसे प्रासंगिक खंड संभवतः है। सिग्नल हैंडलर का मेमोरी प्रबंधन। यह खंड सलाह देता है:

जबकि सिग्नल हैंडलर स्वचालित रूप से डिस्कनेक्ट हो जाते हैं जब सिग्नल उत्सर्जित करने वाली वस्तु को अंतिम रूप दिया जाता है, जब सिग्नल हैंडलर उपयोगकर्ता डेटा नष्ट हो जाता है तो वे स्वचालित रूप से डिस्कनेक्ट नहीं होते हैं ... ऐसे उपयोगकर्ता डेटा के प्रबंधन के लिए दो रणनीतियां हैं। पहला है सिग्नल हैंडलर को डिस्कनेक्ट करना (g_signal_handler_disconnect() या g_signal_handlers_disconnect_by_func() का उपयोग करके) जब उपयोगकर्ता डेटा (ऑब्जेक्ट) को अंतिम रूप दिया जाता है; इसे मैन्युअल रूप से लागू किया जाना है। गैर-थ्रेडेड प्रोग्राम के लिए, g_signal_connect_object() का उपयोग इसे स्वचालित रूप से लागू करने के लिए किया जा सकता है... दूसरा एक मजबूत संदर्भ रखने के लिए है...पहला दृष्टिकोण अनुशंसित है...

Vala कैसे काम करता है, इस बारे में गहराई से जानने के लिए --ccode स्विच valac के साथ है। यदि आप अपने प्रोग्राम के सी कोड को देखते हैं तो आप देखेंगे कि foo_construct फ़ंक्शन में g_signal_connect_object() को कॉल शामिल है। तो वाला प्रलेखन में उल्लिखित पहले दृष्टिकोण का उपयोग कर रहा है, लेकिन तीन फ़ंक्शन कॉलों का सुझाव दिया गया है कि वाला g_signal_connect_object() को कॉल करके स्वचालित का उपयोग कर रहा है।

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

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

3
AlThomas 19 अगस्त 2016, 20:41
आपके सूचनात्मक उत्तर के लिए बहुत-बहुत धन्यवाद, लेकिन मुझे वास्तव में यकीन नहीं है कि यह कैसे बताता है कि Foo के विनाशक से बुलाए जाने पर g_signal_handler_disconnect विफल क्यों होता है, जो वास्तव में इंगित करता है कि वस्तु को अंतिम रूप दिया जा रहा है, कम से कम में मेरी समझ?
 – 
theV0ID
19 अगस्त 2016, 16:14
दस्तावेज़ीकरण से मेरे उद्धरण में मैंने g_signal_connect_object पर वाक्य संपादित किया। मैंने इसे शामिल करने के लिए अपना जवाब संपादित कर लिया है। उम्मीद है कि यह बताता है कि आप अपने कोड में क्या देख रहे हैं।
 – 
AlThomas
19 अगस्त 2016, 19:45
बहुत बहुत शुक्रिया! यह निश्चित रूप से टिप्पणियों की व्याख्या करता है। हालांकि मुझे लगता है, आपका मतलब था "... GLib द्वारा प्रदान किया गया स्वचालित तरीका g_signal_connect_object" को कॉल करके "... g_signal_handler_disconnect", है ना? साथ ही, मैं यह नहीं देखता कि वाला "पहला दृष्टिकोण" का कितना दूर उपयोग कर रहा है, क्योंकि जहां तक ​​मैं अब समझता हूं, "पहला दृष्टिकोण" होगा इसे g_signal_handler_disconnect का उपयोग करके मैन्युअल रूप से करें?
 – 
theV0ID
19 अगस्त 2016, 20:25
1
प्रलेखन में बात की गई पहली दृष्टिकोण सिग्नल को डिस्कनेक्ट करना है और इसमें ऐसा करने के लिए तीन अलग-अलग फ़ंक्शन कॉल का उल्लेख है। दूसरा तरीका है "दूसरा उपयोगकर्ता डेटा पर एक मजबूत संदर्भ रखना है जब तक कि अन्य कारणों से सिग्नल डिस्कनेक्ट नहीं हो जाता है। इसे g_signal_connect_data() का उपयोग करके स्वचालित रूप से कार्यान्वित किया जा सकता है।" वह मेरा पढ़ना था। इसलिए मैंने कहा कि दस्तावेज़ीकरण भ्रामक लग सकता है।
 – 
AlThomas
19 अगस्त 2016, 20:36