ऑब्जेक्टिव-सी में जीसीडी के डिस्पैच_ऑनस का उपयोग करके सिंगलटन बनाएं


341

यदि आप iOS 4.0 या उससे ऊपर का लक्ष्य बना सकते हैं

जीसीडी का उपयोग करना, क्या यह उद्देश्य-सी (धागा सुरक्षित) में सिंगलटन बनाने का सबसे अच्छा तरीका है?

+ (instancetype)sharedInstance
{
    static dispatch_once_t once;
    static id sharedInstance;
    dispatch_once(&once, ^{
        sharedInstance = [[self alloc] init];
    });
    return sharedInstance;
}

2
क्या क्लास के उपयोगकर्ताओं को कॉल आवंटन / कॉपी से रोकने का कोई तरीका है?
निकोलस मियारी

3
dispatch_once_t और dispatch_once 4.0 में प्रस्तुत किए गए प्रतीत होते हैं, 4.1 नहीं (देखें: developer.apple.com/library/ios/#documentation/Performance/… )
बेन फ्लिन

1
यह विधि समस्याग्रस्त हो जाती है अगर init को सिंगलटन ऑब्जेक्ट के उपयोग की आवश्यकता होती है। मैट गैलाघेर के कोड ने मेरे लिए कुछ मौकों पर काम किया है। cocoawithlove.com/2008/11/…
greg

1
मैं इस उदाहरण में इसकी असंगतता को जानता हूं; लेकिन लोग 'नए' का अधिक उपयोग क्यों नहीं करते हैं। dispatch_once (और एक बार, ^ {shareInstance = [self new];} बस उस बिट नटर को दिखता है। यह आवंटन + init के बराबर है।
क्रिस हैटन

3
वापसी प्रकार का उपयोग शुरू करना सुनिश्चित करें instancetype। कोड पूरा होने के बजाय इसका उपयोग करते समय बहुत बेहतर है id
श्री रोजर्स

जवाबों:


215

यह आपकी कक्षा का एक उदाहरण बनाने के लिए पूरी तरह से स्वीकार्य और थ्रेड-सुरक्षित तरीका है। यह तकनीकी रूप से एक "सिंगलटन" नहीं हो सकता है (इसमें इनमें से केवल 1 वस्तु हो सकती है), लेकिन जब तक आप केवल [Foo sharedFoo]वस्तु तक पहुंचने के लिए विधि का उपयोग करते हैं, यह काफी अच्छा है।


4
हालांकि आप इसे कैसे जारी करते हैं?
samvermette

65
@samvermette तुम नहीं। एक सिंगलटन की बात यह है कि यह हमेशा मौजूद रहेगा। इस प्रकार, आप इसे जारी नहीं करते हैं, और स्मृति प्रक्रिया से बाहर निकल जाती है।
डेव डोंगलांग

6
@ डेवॉन्ग: मेरे विचार से सिंगलटन होने का उद्देश्य इसकी अमरता की निश्चितता नहीं है, लेकिन निश्चितता है कि हमारे पास एक उदाहरण है। क्या होगा अगर उस सिंगलटन ने एक सेमाफोर को कम किया हो? आप सिर्फ यह नहीं कह सकते कि यह हमेशा मौजूद रहेगा।
jacekmigacz

4
@hooleyhoop हाँ, इसके प्रलेखन में । "यदि एक साथ कई थ्रेड्स से कॉल किया जाता है, तो यह फ़ंक्शन ब्लॉक के पूरा होने तक समकालिक रूप से प्रतीक्षा करता है।"
केविन

3
@ WalterMartinVargas-Pena का स्थिर संदर्भ वैरिएबल वैरिएबल द्वारा होता है
डेव डोंगलोंग

36

instancetype

instancetypeकई भाषा एक्सटेंशनों में से सिर्फ एक है Objective-C, जिसमें प्रत्येक नई रिलीज़ के साथ अधिक जोड़ा जाता है।

इसे जानो, इसे प्यार करो।

और इसे एक उदाहरण के रूप में लें कि निम्न-स्तरीय विवरणों पर ध्यान देना आपको उद्देश्य-सी को बदलने के लिए शक्तिशाली नए तरीकों से अंतर्दृष्टि प्रदान कर सकता है।

यहां देखें: इंस्टेंक्टाइप


+ (instancetype)sharedInstance
{
    static dispatch_once_t once;
    static id sharedInstance;

    dispatch_once(&once, ^
    {
        sharedInstance = [self new];
    });    
    return sharedInstance;
}

+ (Class*)sharedInstance
{
    static dispatch_once_t once;
    static Class *sharedInstance;

    dispatch_once(&once, ^
    {
        sharedInstance = [self new];
    });    
    return sharedInstance;
}

4
अद्भुत टिप, धन्यवाद! instancetype एक प्रासंगिक कीवर्ड है, जिसका उपयोग परिणाम प्रकार के रूप में संकेत देने के लिए किया जा सकता है कि कोई विधि संबंधित परिणाम प्रकार देता है। ... इंस्टेंटाइप के साथ, कंपाइलर सही प्रकार से अनुमान लगाएगा।
फटी

1
यह मेरे लिए स्पष्ट नहीं है कि दोनों स्निपेट का क्या मतलब है, क्या वे एक दूसरे के बराबर हैं? एक दूसरे के लिए बेहतर है? अच्छा होगा यदि लेखक इसके लिए थोड़ा स्पष्टीकरण जोड़ सके।
गैलेक्टिका

33

MySingleton.h

@interface MySingleton : NSObject

+(instancetype)sharedInstance;

+(instancetype)alloc __attribute__((unavailable("alloc not available, call sharedInstance instead")));
-(instancetype)init __attribute__((unavailable("init not available, call sharedInstance instead")));
+(instancetype)new __attribute__((unavailable("new not available, call sharedInstance instead")));
-(instancetype)copy __attribute__((unavailable("copy not available, call sharedInstance instead")));

@end

MySingleton.m

@implementation MySingleton

+(instancetype)sharedInstance {
    static dispatch_once_t pred;
    static id shared = nil;
    dispatch_once(&pred, ^{
        shared = [[super alloc] initUniqueInstance];
    });
    return shared;
}

-(instancetype)initUniqueInstance {
    return [super init];
}

@end

Init कैसे उपलब्ध नहीं है? क्या यह कम से कम एक के लिए उपलब्ध नहीं है init?
हनी

2
सिंगलटन के पास केवल एक एक्सेस प्वाइंट होना चाहिए। और यह बिंदु साझा है। अगर हमारे पास * .h फ़ाइल में init विधि है तो आप एक और सिंगलटन उदाहरण बना सकते हैं। यह एक सिंगलटन की परिभाषा का खंडन करता है।
सर्गेई पेट्रुक

1
@ asma22 __attribute __ ((अनुपलब्ध) () इन विधियों का उपयोग करने के लिए उपलब्ध नहीं है। यदि कोई अन्य प्रोग्रामर अनुपलब्ध के रूप में चिह्नित विधि का उपयोग करना चाहता है, तो उसे त्रुटि मिलती है
सर्गेई पेट्रुक

1
मैं पूरी तरह से मिलता हूं और मुझे खुशी है कि मैंने कुछ नया सीखा है, आपके उत्तर में कुछ भी गलत नहीं है, बस कुछ नया करने के लिए भ्रमित हो सकता है ...
Honey

1
यह केवल के लिए काम करता है MySingleton, उदाहरण के लिए MySingleton.mमैं कॉल कर रहा हूं[super alloc]
सेर्गेई पेट्रुक

6

आप इस बात से बच सकते हैं कि वर्ग को आवंटित पद्धति को ओवरराइट करने के साथ आवंटित किया जाए।

@implementation MyClass

static BOOL useinside = NO;
static id _sharedObject = nil;


+(id) alloc {
    if (!useinside) {
        @throw [NSException exceptionWithName:@"Singleton Vialotaion" reason:@"You are violating the singleton class usage. Please call +sharedInstance method" userInfo:nil];
    }
    else {
        return [super alloc];
    }
}

+(id)sharedInstance
{
    static dispatch_once_t p = 0;
    dispatch_once(&p, ^{
        useinside = YES;
        _sharedObject = [[MyClass alloc] init];
        useinside = NO;
    });   
    // returns the same object each time
    return _sharedObject;
}

1
यह ऊपर दिए गए टिप्पणियों में मेरे सवाल का जवाब देता है। ऐसा नहीं है कि मैं रक्षात्मक प्रोग्रामिंग के लिए बहुत कुछ हूं, लेकिन ...
निकोलस मारी

5

डेव सही है, यह पूरी तरह से ठीक है। आप कुछ अन्य तरीकों को लागू करने के लिए युक्तियों के लिए एक सिंगलटन बनाने पर ऐप्पल के डॉक्स को देखना चाहते हैं ताकि यह सुनिश्चित हो सके कि केवल एक ही बनाया जा सकता है यदि कक्षाएं साझाफू विधि का उपयोग करने के लिए नहीं चुनते हैं।


8
एह ... यह एक सिंगलटन बनाने का सबसे बड़ा उदाहरण नहीं है। स्मृति प्रबंधन विधियों को ओवरराइड करना आवश्यक नहीं है।
डेव डेलॉन्ग

19
यह ARC का उपयोग करके पूरी तरह से अमान्य है।
लोगानकत्रेल

तब से उद्धृत दस्तावेज़ उद्धृत किया गया है। साथ ही, जो उत्तर केवल बाहरी सामग्री से लिंक होते हैं, वे आम तौर पर खराब SO उत्तर होते हैं। आपके उत्तर के भीतर न्यूनतम अंश संबंधित अंश में। जब तक आप पुराने तरीके को पोस्टीरिटी के लिए बचाना चाहते हैं, यहां परेशान न करें।
टूलबार

4

यदि आप यह सुनिश्चित करना चाहते हैं कि [[MyClass आबंटित init] साझा वस्तु के रूप में एक ही वस्तु लौटाता है (मेरी राय में आवश्यक नहीं है, लेकिन कुछ लोग इसे चाहते हैं), जो कि बहुत आसानी से और सुरक्षित रूप से दूसरे डिस्पैच_ऑन का उपयोग करके किया जा सकता है:

- (instancetype)init
{
    static dispatch_once_t once;
    static Class *sharedInstance;

    dispatch_once(&once, ^
    {
        // Your normal init code goes here. 
        sharedInstance = self;
    });

    return sharedInstance;
}

यह [[MyClass आवंटित] init] और [MyClass साझाInstance] के किसी भी संयोजन को एक ही वस्तु को वापस करने की अनुमति देता है; [MyClass sharedInstance] बस थोड़ा और कुशल होगा। यह कैसे काम करता है: [MyClass साझा किया गया] एक बार [[MyClass आवंटित] init] कॉल करेगा। अन्य कोड इसे भी कह सकते हैं, किसी भी समय। पहला कॉलर इनिट "सामान्य" आरंभीकरण करेगा और सिंगलटन ऑब्जेक्ट को इनिट विधि से दूर रखेगा। किसी भी बाद में कॉल init पूरी तरह से अनदेखा कर देगा कि क्या आवंटन वापस लौटा और समान सामायिक लौटाता है; आवंटन के परिणाम से निपटा जाएगा।

+ साझाकरण विधि हमेशा की तरह काम करेगी। यदि [[MyClass आवंटित] init] को कॉल करने वाला यह पहला कॉलर नहीं है, तो init का परिणाम आवंटन कॉल का परिणाम नहीं है, लेकिन यह ठीक है।


2

आप पूछते हैं कि क्या यह "सिंगलटन बनाने का सबसे अच्छा तरीका" है।

कुछ विचार:

  1. सबसे पहले, हाँ, यह एक धागा-सुरक्षित समाधान है। यह dispatch_onceपैटर्न ऑब्जेक्टिव-सी में सिंगललेट जनरेट करने का आधुनिक, थ्रेड-सुरक्षित तरीका है। वहां कोई चिंता नहीं।

  2. आपने पूछा, हालांकि, क्या यह करने का "सबसे अच्छा" तरीका है। एक को स्वीकार करना चाहिए, हालांकि, कि instancetypeऔर[[self alloc] init] सिंग्लेट्स के साथ संयोजन में उपयोग किए जाने पर भ्रामक है।

    इसका लाभ instancetypeयह है कि यह घोषित करने का एक नायाब तरीका है कि एक प्रकार का सहारा लिए बिना वर्ग को उपवर्गित किया जा सकता है।id , जैसे कि हमें याचना में करना था।

    लेकिन staticइस पद्धति में उपवर्ग चुनौतियों को प्रस्तुत करता है। क्या होगा अगर ImageCacheऔर BlobCacheसिंगलटन दोनों एक Cacheसुपरक्लास से अपने स्वयं के sharedCacheतरीके को लागू किए बिना उपवर्ग थे ?

    ImageCache *imageCache = [ImageCache sharedCache];  // fine
    BlobCache *blobCache = [BlobCache sharedCache];     // error; this will return the aforementioned ImageCache!!!

    इस काम के लिए, आपको यह सुनिश्चित करना होगा कि उपवर्ग अपने स्वयं के sharedInstance(या जो भी आप इसे अपने विशेष वर्ग के लिए कहते हैं) विधि को लागू करें।

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

  3. स्विफ्ट के साथ सर्वश्रेष्ठ अंतर के लिए, आप शायद इसे एक संपत्ति के रूप में परिभाषित करना चाहते हैं, न कि एक वर्ग विधि, जैसे:

    @interface Foo : NSObject
    @property (class, readonly, strong) Foo *sharedFoo;
    @end

    फिर आप आगे जा सकते हैं और इस संपत्ति के लिए एक लिखने वाला लिख ​​सकते हैं (कार्यान्वयन dispatch_onceआपके द्वारा सुझाए गए पैटर्न का उपयोग करेगा ):

    + (Foo *)sharedFoo { ... }

    इसका लाभ यह है कि यदि कोई स्विफ्ट उपयोगकर्ता इसका उपयोग करने जाता है, तो वे कुछ ऐसा करेंगे:

    let foo = Foo.shared

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

    एक तरफ के रूप में, यदि आप यह देखते हैं कि Apple अपने सिंगलेट्स को कैसे परिभाषित कर रहा है, तो यह पैटर्न है जिसे उन्होंने अपनाया है, उदाहरण के लिए उनके NSURLSessionसिंगलटन को इस प्रकार परिभाषित किया गया है:

    @property (class, readonly, strong) NSURLSession *sharedSession;
  4. एक और, बहुत मामूली स्विफ्ट इंटरऑपरेबिलिटी विचार सिंगलटन का नाम था। यह सबसे अच्छा है यदि आप टाइप के नाम को शामिल कर सकते हैं, बजाय sharedInstance। उदाहरण के लिए, यदि वर्ग था Foo, तो आप एकल संपत्ति को परिभाषित कर सकते हैं sharedFoo। या यदि वर्ग था DatabaseManager, तो आप संपत्ति को कॉल कर सकते हैं sharedManager। तब स्विफ्ट उपयोगकर्ता कर सकते हैं:

    let foo = Foo.shared
    let manager = DatabaseManager.shared

    स्पष्ट रूप से, यदि आप वास्तव में उपयोग करना चाहते हैं sharedInstance, तो आप हमेशा स्विफ्ट नाम की घोषणा कर सकते हैं जो आपको चाहिए:

    @property (class, readonly, strong) Foo* sharedInstance NS_SWIFT_NAME(shared);

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

  5. मैं उन अन्य लोगों से सहमत हूं जो इंगित करते हैं कि यदि आप चाहते हैं कि यह एक सच्चा सिंगलटन हो, जहां डेवलपर्स (गलती से) अपने स्वयं के उदाहरणों को unavailableप्राप्त नहीं कर सकते हैं, तो क्वालिफायर चालू है initऔर newविवेकपूर्ण है।


0

धागा सुरक्षित सिंगलटन बनाने के लिए आप ऐसा कर सकते हैं:

@interface SomeManager : NSObject
+ (id)sharedManager;
@end

/* thread safe */
@implementation SomeManager

static id sharedManager = nil;

+ (void)initialize {
    if (self == [SomeManager class]) {
        sharedManager = [[self alloc] init];
    }
}

+ (id)sharedManager {
    return sharedManager;
}
@end

और यह ब्लॉग सिंगलटन को objc / कोको में बहुत अच्छी तरह से सिंगलटन समझाता है


आप एक बहुत पुराने लेख से जुड़ रहे हैं जबकि ओपी सबसे आधुनिक कार्यान्वयन के बारे में विशेषताओं के बारे में पूछता है।
वाइकिंगोजगुंडो

1
प्रश्न एक विशिष्ट कार्यान्वयन के बारे में है। आप बस एक और कार्यान्वयन पोस्ट करें। वहाँ-के लिए आप भी सवाल का जवाब देने की कोशिश नहीं करते।
vikingosegundo

1
@vikingosegundo पूछने वाला मौसम से पूछते हैं कि GCD थ्रेड सुरक्षित सिंगलटन बनाने का सबसे अच्छा तरीका है, मेरा जवाब एक और विकल्प देता है। गलत है?
हैनकॉक_Xu

पूछनेवाला पूछता है कि क्या एक निश्चित कार्यान्वयन थ्रेड-सुरक्षित है। वह विकल्प नहीं मांग रहा है।
vikingosegundo

0
//Create Singleton  
  +( instancetype )defaultDBManager
    {

        static dispatch_once_t onceToken = 0;
        __strong static id _sharedObject = nil;

        dispatch_once(&onceToken, ^{
            _sharedObject = [[self alloc] init];
        });

        return _sharedObject;
    }


//In it method
-(instancetype)init
{
    self = [super init];
  if(self)
     {
   //Do your custom initialization
     }
     return self;
}

0
@interface className : NSObject{
+(className*)SingleTonShare;
}

@implementation className

+(className*)SingleTonShare{

static className* sharedObj = nil;
static dispatch_once_t once = 0;
dispatch_once(&once, ^{

if (sharedObj == nil){
    sharedObj = [[className alloc] init];
}
  });
     return sharedObj;
}
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.