Apple ARC के तहत सिंगलटन पैटर्न को लागू करने के लिए dispatch_once का उपयोग करने की सिफारिश क्यों करता है?


305

एआरसी के तहत एक सिंगलटन के साझा उदाहरण एक्सेसर में डिस्पैच_ऑन का उपयोग करने का सटीक कारण क्या है?

+ (MyClass *)sharedInstance
{
    //  Static local predicate must be initialized to 0
    static MyClass *sharedInstance = nil;
    static dispatch_once_t onceToken = 0;
    dispatch_once(&onceToken, ^{
        sharedInstance = [[MyClass alloc] init];
        // Do any other initialisation stuff here
    });
    return sharedInstance;
}

क्या पृष्ठभूमि में सिंगलटन को अतुल्यकालिक रूप से इंस्टेंट करना बुरा नहीं है? मेरा मतलब है कि क्या होगा अगर मैं उस साझा उदाहरण का अनुरोध करता हूं और उस पर तुरंत भरोसा करता हूं, लेकिन डिस्पैच_ऑनस मेरी वस्तु बनाने के लिए क्रिसमस तक लेता है? यह तुरंत सही नहीं है? कम से कम यह ग्रैंड सेंट्रल डिस्पैच का पूरा बिंदु लगता है।

तो वे ऐसा क्यों कर रहे हैं?


Note: static and global variables default to zero.
ikkentim

जवाबों:


418

dispatch_once()बिल्कुल तुल्यकालिक है। सभी जीसीडी विधियां एसिंक्रोनस रूप से नहीं करती हैं (बिंदु में मामला dispatch_sync()समकालिक है)। dispatch_once()निम्नलिखित मुहावरों की जगह उपयोग :

+ (MyClass *)sharedInstance {
    static MyClass *sharedInstance;
    @synchronized(self) {
        if (sharedInstance == nil) {
            sharedInstance = [[MyClass alloc] init];
        }
    }
    return sharedInstance;
}

इस पर लाभ dispatch_once()यह है कि यह तेज है। यह शब्दार्थिक रूप से क्लीनर भी है, क्योंकि यह आपके साझा किए गए इनस्टेंस के इन-ऑलिट को आवंटित करने वाले कई थ्रेड्स से भी बचाता है - यदि वे सभी एक ही सटीक समय पर प्रयास करते हैं। यह दो उदाहरणों को बनाने की अनुमति नहीं देगा। संपूर्ण विचार यह है dispatch_once()कि "एक बार और केवल एक बार कुछ प्रदर्शन करें", जो कि ठीक है कि हम क्या कर रहे हैं।


4
तर्क के लिए मुझे यह ध्यान देने की आवश्यकता है कि दस्तावेज़ीकरण यह नहीं कहता है कि इसे सिंक्रोनाइज़ किया जा रहा है। यह केवल यह कहता है कि एक साथ कई आक्रमणों को क्रमबद्ध किया जाएगा।
विशेषज्ञ

5
आप दावा करते हैं कि यह तेज़ है - कितना तेज़ है? मेरे पास यह सोचने का कोई कारण नहीं है कि आप सच नहीं बता रहे हैं, लेकिन मैं एक साधारण बेंचमार्क देखना चाहूंगा।
जोशुआ ग्रॉस

29
मैंने सिर्फ एक साधारण बेंचमार्क (iPhone 5 पर) किया था और ऐसा लग रहा है कि डिस्पैच_ऑनसे @synchronized से लगभग 2x तेज है।
जोशुआ ग्रॉस

3
@ रेनेडोहान: यदि आप 100% निश्चित हैं कि कोई भी कभी भी एक अलग धागे से उस विधि को नहीं कहता है, तो यह काम करता है। लेकिन इसका उपयोग dispatch_once()करना वास्तव में सरल है (विशेषकर क्योंकि Xcode इसे आपके लिए पूर्ण कोड स्निपेट में स्वतः पूर्ण कर देगा) और इसका मतलब है कि आपको कभी भी इस बात पर विचार करने की आवश्यकता नहीं है कि क्या विधि को थ्रेड-सुरक्षित होने की आवश्यकता है।
लिली बॉलर

1
@ अनुनय: वास्तव में, यह सच नहीं है। सबसे पहले, +initializeवर्ग को छूने से पहले कुछ भी किया जाता है, भले ही आप अभी तक अपना साझा उदाहरण बनाने की कोशिश नहीं कर रहे हों। सामान्य तौर पर, आलसी इनिशियलाइज़ेशन (केवल ज़रूरत पड़ने पर कुछ बनाना) बेहतर है। दूसरा, यहां तक ​​कि आपके प्रदर्शन का दावा भी सही नहीं है। dispatch_once()कहने if (self == [MyClass class])में लगभग उतना ही समय लगता है +initialize। यदि आपके पास पहले से ही एक है +initialize, तो हाँ साझा उदाहरण बनाने से तेज़ है, लेकिन अधिकांश कक्षाएं नहीं होती हैं।
लिली बॉलार्ड

41

क्योंकि यह केवल एक बार ही चलेगा। इसलिए यदि आप इसे अलग-अलग धागों से दो बार आज़माते हैं और इसका उपयोग करते हैं, तो इससे कोई समस्या नहीं होगी।

माइक ऐश ने अपनी केयर और फीडिंग ऑफ सिंगलेट्स ब्लॉग पोस्ट में पूरा विवरण दिया है ।

सभी GCD ब्लॉक एसिंक्रोनस रूप से नहीं चलते हैं।


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