2010-02-19 11 views
6

सिंगलटन साहित्य पर पोरिंग के बाद, मैंने जो कुछ बनाया है, वह यहां है। क्या मैं कुछ भूल गया हूँ?क्या यह निश्चित रेफ गिना गया उद्देश्य सी सिंगलटन कार्यान्वयन है?

@implementation MySingleton 

static MySingleton *mySharedInstance = nil; 

//called by atexit on exit, to ensure all resources are freed properly (not just memory) 
static void singleton_remover() 
{ 
    //free resources here 
} 

+ (MySingleton*) sharedInstance{ 
    return mySharedInstance; 
} 

+ (void)initialize { 
    if (self == [MySingleton class]) { 
     mySharedInstance = [[super allocWithZone:NULL] init]; 
    atexit(singleton_remover);  
    } 
} 

+ (id)allocWithZone:(NSZone *)zone 
{ 
    return [self sharedInstance]; 
} 

- (id)copyWithZone:(NSZone *)zone 
{ 
    return self;  
} 

- (id)retain 
{ 
    return self;  
} 

- (NSUInteger)retainCount 
{ 
    return NSUIntegerMax; //denotes an object that cannot be released 
} 

- (void)release 
{ 
    //do nothing  
} 

- (id)autorelease 
{ 
    return self;  
} 
+1

कृपया अपने प्रश्न को ऐसे तरीके से संपादित न करें जो मौजूदा उत्तरों को अमान्य बनाता है या अब प्रासंगिक नहीं है। – dreamlax

+0

क्यों एक और उद्देश्य-सी सिंगलटन धागा? उदाहरण के लिए http://stackoverflow.com/questions/145154 देखें। – zoul

+0

क्या आप गैलाघर का उपयोग नहीं करेंगे .. http://proocsithlove.com/projects/SynthesizeSingleton.h.zip http://cocoawithlove.com/2008/11/singletons-appdelegates-and-top-level.html से। ..? अपरिवर्तनीयता और ज्वालामुखी के लिए – Fattie

उत्तर

2

जो सिंक्रनाइज़ ताला समय

आप अपने सॉफ़्टवेयर विश्वसनीय होना चाहते हैं, निर्माणों कि काम से बचने का सबसे से बचा जाता है

http://developer.apple.com/mac/library/DOCUMENTATION/Cocoa/Conceptual/Multithreading/ThreadSafety/ThreadSafety.html

"समय की सबसे"

तालिका 4.1। डबल-चेक लॉक

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

+0

-1। –

+0

मुझे लगता है कि वह सोच रहा था "ज्यादातर समय सीपीयू और सिंक्रनाइज़ेशन बचाता है"। हालांकि, आधुनिक सीपीयू और ऑप्टिमाइज़र के साथ यह बहुत अच्छी तरह से "सही समय की सही चीज़" हो सकता है। लेकिन मैं स्वीकार करूंगा कि विंडोज़ टिप्पणी अनावश्यक थी। – Darron

+0

लेकिन डबल-चेक लॉकिंग _is_ अविश्वसनीय, या क्या मैं गलत हूं? * संपादित करें: * मैं सही हूँ। http://developer.apple.com/mac/library/DOCUMENTATION/Cocoa/Conceptual/Multithreading/ThreadSafety/ThreadSafety.html – glebm

1

कुछ सुझाव (iPhone से मैक कोको के लिए और अधिक है, लेकिन यह अन्य लोगों को खोज उद्देश्य-सी टैग करने के लिए उपयोगी हो सकता है): अभी शून्य,:

  • -allocWithZone साथ परेशान न हों सादा -alloc ठीक कर देगा। के बाद से है कि प्रभावी ढंग से एप्लिकेशन -9s मारने
  • , dispatch_once() या pthread_once() जहां उपलब्ध
  • atexit उपयोग चालाक है उपयोग पर विचार करें, लेकिन तेजी से एप्लिकेशन समाप्ति (यकीन नहीं अगर यह iPhone पर लागू होता है) के साथ संगत नहीं हो सकता है

एक अतिरिक्त मज़ा पैटर्न:

+ (Foo *)sharedFoo { 
    static Foo *sharedInstance = NULL; 
    if (!sharedInstance) { 
     Foo *temp = [[Foo alloc] init]; //NOTE: This MUST NOT have side effects for it to be threadsafe 
     if (!OSAtomicCompareAndSwapPtrBarrier(NULL, temp, &sharedInstance)) { 
      [temp release]; 
     } 
    } 
    return sharedInstance; 
} 
+0

- ऑलोक का उपयोग करके मेरे ऑलोक विथज़ोन ओवरराइड को कॉल किया जाएगा, जो साझा किए गए इंस्टेंस पर शून्य होगा, इसलिए यह एक समस्या होगी – Jacko

+0

आह, निश्चित रूप से। उस बिट को अनदेखा करें :) –

0

singleton_remover समारोह, कुछ नहीं करेंगे क्योंकि आप release अधिरोहित किया है कुछ भी नहीं करना। आपकी allocWithZone: विधि साझा उदाहरण में retain भेजती है (और निर्दिष्ट क्षेत्र में पूरी तरह से आवंटन को पूरी तरह से अनदेखा करती है) पर एक समान नो-ऑप भी करती है। शायद आपके पास एक ध्वज होना चाहिए जो आपके साझा उदाहरण अजेय है (यानी अविश्वसनीय) या नहीं।

किसी भी तरह से, ओएस वैसे भी सभी मेमोरी साफ़ कर देगा। प्रलेखन में कहा गया है कि ओएस के लिए यह आपके कंप्यूटर के धीरे-धीरे टुकड़े से टुकड़े टुकड़े करने के लिए सभी मेमोरी को पुनः प्राप्त करने के लिए तेज़ है।

अपने साझा उदाहरण संसाधन है कि हमेशा साफ किया जा करने के लिए जब अपने आवेदन समाप्त हो जाता है की जरूरत प्रबंध कर रहा है, तो आप इसे प्राप्त करने के लिए रजिस्टर है, और वहाँ में सफाई करना चाहिए।

[[NSNotificationCenter defaultCenter] addObserver:self 
             selector:@selector(performCleanup:) 
              name:UIApplicationWillTerminateNotification 
              object:nil]; 
+0

धन्यवाद। सिंगलटन_remover विधि में संसाधनों को जारी करने में क्या गलत है? – Jacko

+0

'एनएस/यूआईएप्लिकेशंस विल्लटर्मिनेट नॉटिफिकेशन' का जवाब आवेदन समाप्ति के दौरान संसाधन प्रबंधन को गहन रूप से संभालने का दस्तावेज तरीका है। – dreamlax

+0

सामान्य मामले में केवल सामान्य थ्रेड पर 'एनएस/यूआईएप्लिकेशन विल्लटर्मिनेट नोटिफिकेशन' के लिए पंजीकरण करना सुरक्षित है - इसे – rpetrich

0

संपादित

मैं शीर्ष पर इस शामिल कर रहा हूँ, तुम मेरे ऐतिहासिक मूल प्रश्न और कार्यान्वयन देख सकते हैं नीचे।हालांकि मुझे लगता है मैं कोई ताला भूमि के ऊपर के साथ एक sharedInstance विधि प्रदान करने के लिए इष्टतम रास्ता मिल गया लगता है, मैं इस बारे में संभावित चिंताओं को सुनने के लिए प्यार होता है:

// Volatile to make sure we are not foiled by CPU caches 
static volatile ALBackendRequestManager *sharedInstance; 

// There's no need to call this directly, as method swizzling in sharedInstance 
// means this will get called after the singleton is initialized. 
+ (MySingleton *)simpleSharedInstance 
{ 
    return (MySingleton *)sharedInstance; 
} 

+ (MySingleton*)sharedInstance 
{ 
    @synchronized(self) 
    { 
     if (sharedInstance == nil) 
     { 
      sharedInstance = [[MySingleton alloc] init]; 
      // Replace expensive thread-safe method 
        // with the simpler one that just returns the allocated instance. 
      SEL orig = @selector(sharedInstance); 
      SEL new = @selector(simpleSharedInstance); 
      Method origMethod = class_getClassMethod(self, orig); 
      Method newMethod = class_getClassMethod(self, new); 
      method_exchangeImplementations(origMethod, newMethod); 
     } 
    } 
    return (MySingleton *)sharedInstance; 
} 

और चारों ओर ऐतिहासिक चर्चा प्रारंभ:

मैं लॉक के बाहर उदाहरण के लिए चेक को छोड़कर, मूल कोड वास्तव में मेरे (नीचे) की तरह था।

हालांकि नई + (शून्य) प्रारंभिक विधि दिलचस्प है, मुझे यकीन नहीं है कि मुझे यह बेहतर पसंद है। ऐसा लगता है कि अब एक सिंगलटन उदाहरण प्राप्त करने के लिए आपको हमेशा कॉल करना होगा:

MySingleton instance = [[MySingleton alloc] init]; 

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

दूसरी बात जो अजीब लगती है, अगर प्रारंभिक विधि वास्तव में पसंद की जाती है तो हम इसे कहीं और क्यों नहीं देखते हैं? उद्देश्य-सी काफी समय से रहा है और मैं मौलिक तंत्र से सावधान हूं जो कि सभी प्रकाशित उदाहरणों से अलग है।

मेरे कोड मैं वर्तमान में (जो दर्पण है कि मैं कहीं और देखा है, this जवाब सहित) का उपयोग करें:

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

+ (id)allocWithZone:(NSZone *)zone 
{ 
    @synchronized(self) 
    { 
     if (sharedInstance == nil) 
     { 
      sharedInstance = [super allocWithZone:zone]; 
      return sharedInstance; // assignment and return on first allocation 
     } 
    } 
    return nil; // on subsequent allocation attempts return nil 
} 
+0

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

+0

@ जैको, यह सच नहीं है, किसी भी संदेश को भेजे जाने से पहले, 'स्वयं' प्रारंभ किया जाएगा (यानी 'प्रारंभिक' के साथ), चाहे वे कक्षा या उदाहरण विधियों का आह्वान करें। – dreamlax

+0

कक्षा स्तर विधि में, "स्वयं" [MyClass क्लास] के समतुल्य है - इसका मतलब है, BOOL iself = self == [MySingleton class]; ऊपर साझा की गई विधि विधि में YES पर सेट किया जाएगा। साथ ही, जैसा कि कोड लिखा गया है, कॉल अनुक्रम की वजह से डेडलॉक्स की कोई संभावना नहीं है ... हालांकि मैं सहमत हूं कि "प्रारंभिक" विधि शायद बेहतर है। कक्षा के बाहर से आप अपने साझा इंटेन्स वैरिएबल पर कैसे जाते हैं? मैंने उस विधि को नहीं देखा। –

0

आपका क्रियान्वयन धागा सुरक्षित है और सभी आधार कवर करने प्रतीत होता है (+ प्रारंभ thread- भेज दिया जाता है रनटाइम द्वारा सुरक्षित)

संपादित करें: atexit फ़ंक्शन के दौरान कॉल करने के लिए बहुत सारे कोड असुरक्षित होंगे। मुख्य धागे पर के लिए पंजीकरण करना सुरक्षित है।

संपादित 2: मैंने मैक्रो में उपयोग किए जाने वाले पैटर्न को आसवित और परिष्कृत किया है। -init को पहली बार +sharedInstance कहा जाता है और -dealloc को एप्लिकेशन समाप्त होने के रूप में बुलाया जाएगा।

#define IMPLEMENT_UIAPP_SINGLETON(class_name) \ 
static class_name *shared ## class_name; \ 
+ (void)cleanupFromTerminate \ 
{ \ 
    class_name *temp = shared ## class_name; \ 
    shared ## class_name = nil; \ 
    [temp dealloc]; \ 
} \ 
+ (void)registerForCleanup \ 
{ \ 
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(cleanupFromTerminate) name:UIApplicationWillTerminateNotification object:nil]; \ 
} \ 
+ (void)initialize { \ 
    if (self == [class_name class]) { \ 
     NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; \ 
     if ([NSThread isMainThread]) \ 
      [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(cleanupFromTerminate) name:UIApplicationWillTerminateNotification object:nil]; \ 
     else \ 
      [self performSelectorOnMainThread:@selector(registerForCleanup) withObject:nil waitUntilDone:NO]; \ 
     shared ## class_name = [[super allocWithZone:NULL] init]; \ 
     [pool drain]; \ 
    } \ 
} \ 
+ (class_name *)sharedInstance \ 
{ \ 
    return shared ## class_name; \ 
} \ 
+ (id)allocWithZone:(NSZone *)zone \ 
{ \ 
    return shared ## class_name; \ 
} \ 
- (id)copyWithZone:(NSZone *)zone \ 
{ \ 
    return self; \ 
} \ 
- (id)retain \ 
{ \ 
    return self; \ 
} \ 
- (NSUInteger)retainCount \ 
{ \ 
    return NSUIntegerMax; \ 
} \ 
- (void)release \ 
{ \ 
} \ 
- (id)autorelease \ 
{ \ 
    return self; \ 
} 
+0

ऐप्पल ने अंडरस्कोर से शुरू होने वाले नामों के साथ विधियों का उपयोग न करने का कहा। अंडरस्कोर उपसर्ग आरक्षित है और ऐप्पल के निजी तरीकों के लिए उपयोग किया जाता है। – dreamlax

+0

बहुत अच्छा बिंदु (http://www.devworld.apple.com/iphone/library/documentation/Cocoa/Conceptual/ObjectiveC/Articles/ocLanguageSummary.html#//apple_ref/doc/uid/TP30001163-CH3-TPXREF108 के अनुसार); – rpetrich

+0

को प्रतिबिंबित करने के लिए संपादित करें आरंभिक विधि में आपको ऑटोरेलीज पूल की आवश्यकता क्यों है? – Jacko