मैं एक उद्देश्य-सी सिंगलटन कैसे लागू कर सकता हूं जो एआरसी के साथ संगत है?


172

Xcode 4.2 में स्वचालित संदर्भ गणना (ARC) का उपयोग करते समय मैं एक एकल वर्ग को कैसे परिवर्तित (या सृजित) करता हूँ जो सही ढंग से संकलित और व्यवहार करता है?


1
मुझे हाल ही में एआरसी और मैनुअल मेमोरी प्रबंधन वातावरण दोनों के लिए सिंगलेट्स पर मैट गैलोवे से काफी गहराई में एक लेख मिला। galloway.me.uk/tutorials/singleton-classes
cescofry

जवाबों:


391

ठीक उसी तरह से जो आप (पहले से) करते रहे हैं:

+ (instancetype)sharedInstance
{
    static MyClass *sharedInstance = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        sharedInstance = [[MyClass alloc] init];
        // Do any other initialisation stuff here
    });
    return sharedInstance;
}

9
आप बस किसी भी स्मृति प्रबंधन hokey pokey Apple का उपयोग नहीं करते हैं, जिसका उपयोग डेवलपर
.apple.com/library/mac/documentation/Cocoa/Conceptual/…

1
@MakingScienceFictionFact, आप इस पोस्ट
kervich

6
staticएक विधि / कार्य के भीतर घोषित @David चर एक विधि / कार्य के staticबाहर घोषित चर के समान हैं, वे केवल उस पद्धति / कार्य के दायरे के भीतर मान्य हैं। +sharedInstanceविधि के माध्यम से प्रत्येक अलग-अलग रन (यहां तक ​​कि विभिन्न थ्रेड्स पर) एक ही sharedInstanceचर को 'देखेंगे' ।
निक फोर्ज

9
क्या होगा अगर कोई [[MyClass आवंटित] init] कहता है? वह एक नई वस्तु का निर्माण करेगा। हम इससे कैसे बच सकते हैं (स्थैतिक मायक्लास की घोषणा के अलावा (साझाकरण = विधि के बाहर शून्य)।
रिकार्डो सांचेज़-साज़

2
यदि कोई अन्य प्रोग्रामर गड़बड़ करता है और जब उसे कॉल किया जाना चाहिए, तो उसे साझा किया जाना चाहिए। दूसरों को संभावित रूप से गलतियां करने से रोकने के लिए भाषा के मूल सिद्धांतों और बुनियादी अनुबंधों को प्रस्तुत करना काफी गलत लगता है। Boredzo.org/blog/archives/2009-06-17/doing-it-wrong
occulus

8

यदि आप आवश्यकतानुसार अन्य उदाहरण बनाना चाहते हैं।

+ (MyClass *)sharedInstance
{
    static MyClass *sharedInstance = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        sharedInstance = [[MyClass alloc] init];
        // Do any other initialisation stuff here
    });
    return sharedInstance;
}

और, आपको यह करना चाहिए:

+ (id)allocWithZone:(NSZone *)zone
{
    static MyClass *sharedInstance = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        sharedInstance = [super allocWithZone:zone];
    });
    return sharedInstance;
}

1
सच / गलत: dispatch_once()बिट का मतलब है कि आपको पहले उदाहरण में भी अतिरिक्त उदाहरण नहीं मिलेंगे ...?
ओली

4
@ ऑली: गलत, क्‍योंकि क्‍लाइंट कोड पहुंच को [[MyClass alloc] init]बायपास और कर sharedInstanceसकता है। डोंगएक्सू, आपको पीटर होसी के सिंगलटन लेख को देखना चाहिए । यदि आप allocWithZone:अधिक इंस्टेंस को बनाए जाने से रोकने के लिए ओवरराइड करने जा रहे हैं , तो आपको initसाझा इंस्टेंस को फिर से आरंभ होने से रोकने के लिए ओवरराइड भी करना चाहिए ।
jscs

ठीक है, यही मैंने सोचा था, इसलिए allocWithZone:संस्करण। धन्यवाद।
ओली

2
यह आवंटन के अनुबंध को पूरी तरह से तोड़ देता है।
15

1
सिंगलटन का अर्थ है "किसी भी समय स्मृति में केवल एक वस्तु", यह एक बात है, फिर से आरंभीकृत होना दूसरी बात है।
डोंगएक्सू


2

यह एआरसी के तहत मेरा पैटर्न है। जीसीडी का उपयोग करके नए पैटर्न को संतुष्ट करता है और एप्पल के पुराने इंस्टेंटेशन रोकथाम पैटर्न को भी संतुष्ट करता है।

@implementation AAA
+ (id)alloc
{
    return  [self allocWithZone:nil];
}
+ (id)allocWithZone:(NSZone *)zone
{
    [self doesNotRecognizeSelector:_cmd];
    abort();
}
+ (instancetype)theController
{
    static AAA* c1  =   nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^
    {
        c1  =   [[super allocWithZone:nil] init];

        // For confirm...       
        NSLog(@"%@", NSStringFromClass([c1 class]));    //  Prints AAA
        NSLog(@"%@", @([c1 class] == self));            //  Prints 1

        Class   real_superclass_obj =   class_getSuperclass(self);
        NSLog(@"%@", @(real_superclass_obj == self));   //  Prints 0
    });

    return  c1;
}
@end

1
क्या यह परिणाम सुपरक्लास c1का उदाहरण नहीं होगा AAA? आप कॉल करने की आवश्यकता +allocपर self, पर नहीं super
निक फोर्ज

@NickForge का superमतलब सुपर-क्लास ऑब्जेक्ट नहीं है। आपको सुपर-क्लास ऑब्जेक्ट नहीं मिल सकता है इसका मतलब है कि सुपर-क्लास संस्करण के लिए संदेशों को रूट करना। superअभी भी अंक selfवर्ग। यदि आप सुपर-क्लास ऑब्जेक्ट प्राप्त करना चाहते हैं, तो आपको रनटाइम प्रतिबिंब कार्यों को प्राप्त करने की आवश्यकता है।
eonil

@NickForge और -allocWithZone:विधि ओवरराइडिंग पॉइंट की पेशकश करने के लिए रनटाइम के आवंटन फ़ंक्शन के लिए एक सरल श्रृंखला है। तो अंततः, selfसूचक == वर्तमान वर्ग ऑब्जेक्ट को आवंटनकर्ता को पास किया जाएगा, और अंत में AAAउदाहरण आवंटित किया जाएगा।
eonil

आप सही हैं, मैं superकक्षा विधियों में काम करने की सूक्ष्मताओं को भूल गया हूं ।
निक फोर्ज

#Import का उपयोग करने के लिए याद रखें <objc / objc-runtime.h>
रयान

2

इस उत्तर को पढ़ें और फिर जाकर दूसरे उत्तर को पढ़ें।

आपको पहले पता होना चाहिए कि एक सिंगलटन का क्या अर्थ है और इसकी आवश्यकताएं क्या हैं, अगर आप इसे नहीं समझते हैं, तो आप समाधान को नहीं समझ पाएंगे - बिल्कुल भी नहीं!

सफलतापूर्वक एक सिंगलटन बनाने के लिए आपको निम्नलिखित 3 करने में सक्षम होना चाहिए:

  • यदि कोई दौड़ की स्थिति थी , तो हमें एक ही समय में आपके साझाकरण के कई उदाहरणों को बनाने की अनुमति नहीं देनी चाहिए!
  • कई इनवोकेशन के बीच मूल्य को याद रखें और रखें।
  • इसे केवल एक बार बनाएं। प्रवेश बिंदु को नियंत्रित करके।

dispatch_once_tकेवल एक बार अपने ब्लॉक को भेजने की अनुमति देकर एक दौड़ की स्थिति को हल करने में आपकी मदद करता है ।

Staticकिसी भी संख्या में इनवोकेशन में "मूल्य" को याद रखने में आपकी मदद करता है। कैसे याद करता है? यह आपके साझा नाम के उस सटीक नाम के साथ किसी भी नए उदाहरण की अनुमति नहीं देता है जिसे फिर से बनाया जाए यह केवल उसी के साथ काम करता है जो मूल रूप से बनाया गया था।

कॉलिंग का उपयोग न करनाalloc init (अर्थात हमारे पास अभी भी alloc initविधियाँ हैं क्योंकि हम एक NSObject उपवर्ग हैं, हालाँकि हमें इनका उपयोग नहीं करना चाहिए) अपने साझाकरण वर्ग पर, हम इसका उपयोग करके प्राप्त करते हैं +(instancetype)sharedInstance, जो केवल एक बार आरंभ करने के लिए बाध्य होता है , विभिन्न थ्रेड्स के कई प्रयासों की परवाह किए बिना। उसी समय और इसके मूल्य को याद रखें।

कोको के साथ आने वाले कुछ सबसे सामान्य सिस्टम सिंगलटन हैं:

  • [UIApplication sharedApplication]
  • [NSUserDefaults standardUserDefaults]
  • [NSFileManager defaultManager]
  • [NSBundle mainBundle]
  • [NSOperations mainQueue]
  • [NSNotificationCenter defaultCenter]

मूल रूप से कुछ भी है कि केंद्रीकृत प्रभाव की आवश्यकता होगी एक सिंगलटन डिजाइन पैटर्न के कुछ प्रकार का पालन करने की आवश्यकता होगी।


1

वैकल्पिक रूप से, Objective-C NSObject और उसके सभी उप-वर्गों के लिए + (शून्य) प्रारंभिक विधि प्रदान करता है। इसे हमेशा कक्षा के किसी भी तरीके से पहले कहा जाता है।

मैंने आईओएस 6 में एक बार एक ब्रेकपॉइंट सेट किया और स्टैक फ्रेम में डिस्पैच_ऑन दिखाई दिया।


0

सिंगलटन क्लास: कोई भी किसी भी मामले में या किसी भी तरह से क्लास की एक से ज्यादा ऑब्जेक्ट नहीं बना सकता है।

+ (instancetype)sharedInstance
{
    static ClassName *sharedInstance = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        sharedInstance = [[ClassName alloc] init];
        // Perform other initialisation...
    });
    return sharedInstance;
}
//    You need need to override init method as well, because developer can call [[MyClass alloc]init] method also. that time also we have to return sharedInstance only. 

-(MyClass)init
{
   return [ClassName sharedInstance];
}

1
यदि कोई व्यक्ति init को कॉल करता है, तो init साझा करेगा। Inance, shareInstance, init को कॉल करेगा, init दूसरी बार साझा कॉल करेगा, तो क्रैश! सबसे पहले, यह एक अनंत पुनरावृत्ति लूप है। दूसरा, डिस्पैच_ऑन को कॉल करने की दूसरी पुनरावृत्ति दुर्घटनाग्रस्त हो जाएगी क्योंकि इसे डिस्पैच_ऑन के अंदर से फिर से कॉल नहीं किया जा सकता है।
चक कृत्सिंगर

0

स्वीकृत उत्तर के साथ दो मुद्दे हैं, जो आपके उद्देश्य के लिए प्रासंगिक हो सकते हैं या नहीं।

  1. यदि init विधि से, किसी भी तरह साझाInstance विधि को फिर से कहा जाता है (जैसे कि अन्य ऑब्जेक्ट्स वहाँ से निर्मित होते हैं जो सिंगलटन का उपयोग करते हैं) यह एक स्टैक अतिप्रवाह का कारण होगा।
  2. श्रेणी पदानुक्रम के लिए केवल एक एकल होता है (अर्थात्: पदानुक्रम में पहली कक्षा जिस पर साझाकरण विधि को कहा जाता था), पदानुक्रम में एक ठोस वर्ग प्रति एक सिंगलटन के बजाय।

निम्नलिखित कोड इन दोनों समस्याओं का ध्यान रखता है:

+ (instancetype)sharedInstance {
    static id mutex = nil;
    static NSMutableDictionary *instances = nil;

    //Initialize the mutex and instances dictionary in a thread safe manner
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        mutex = [NSObject new];
        instances = [NSMutableDictionary new];
    });

    id instance = nil;

    //Now synchronize on the mutex
    //Note: do not synchronize on self, since self may differ depending on which class this method is called on
    @synchronized(mutex) {
        id <NSCopying> key = (id <NSCopying>)self;
        instance = instances[key];
        if (instance == nil) {
            //Break allocation and initialization into two statements to prevent a stack overflow, if init somehow calls the sharedInstance method
            id allocatedInstance = [self alloc];

            //Store the instance into the dictionary, one per concrete class (class acts as key for the dictionary)
            //Do this right after allocation to avoid the stackoverflow problem
            if (allocatedInstance != nil) {
                instances[key] = allocatedInstance;
            }
            instance = [allocatedInstance init];

            //Following code may be overly cautious
            if (instance != allocatedInstance) {
                //Somehow the init method did not return the same instance as the alloc method
                if (instance == nil) {
                    //If init returns nil: immediately remove the instance again
                    [instances removeObjectForKey:key];
                } else {
                    //Else: put the instance in the dictionary instead of the allocatedInstance
                    instances[key] = instance;
                }
            }
        }
    }
    return instance;
}

-2
#import <Foundation/Foundation.h>

@interface SingleTon : NSObject

@property (nonatomic,strong) NSString *name;
+(SingleTon *) theSingleTon;

@end

#import "SingleTon.h"
@implementation SingleTon

+(SingleTon *) theSingleTon{
    static SingleTon *theSingleTon = nil;

    if (!theSingleTon) {

        theSingleTon = [[super allocWithZone:nil] init
                     ];
    }
    return theSingleTon;
}

+(id)allocWithZone:(struct _NSZone *)zone{

    return [self theSingleTon];
}

-(id)init{

    self = [super init];
    if (self) {
        // Set Variables
        _name = @"Kiran";
    }

    return self;
}

@end

कोड से ऊपर की आशा इसे बाहर लाने में मदद करेगी।


-2

अगर आपको स्विफ्ट में सिंगलटन बनाने की आवश्यकता है,

class var sharedInstance: MyClass {
    struct Singleton {
        static let instance = MyClass()
    }
    return Singleton.instance
}

या

struct Singleton {
    static let sharedInstance = MyClass()
}

class var sharedInstance: MyClass {
    return Singleton.sharedInstance
}

आप इस तरह से उपयोग कर सकते हैं

let sharedClass = LibraryAPI.sharedInstance
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.