ما الذي يجب أن تبدو عليه مفردتي Objective-C؟



Answers

@interface MySingleton : NSObject
{
}

+ (MySingleton *)sharedSingleton;
@end

@implementation MySingleton

+ (MySingleton *)sharedSingleton
{
  static MySingleton *sharedSingleton;

  @synchronized(self)
  {
    if (!sharedSingleton)
      sharedSingleton = [[MySingleton alloc] init];

    return sharedSingleton;
  }
}

@end

[Source]

Question

عادة ما تكون طريقة accessor المنفصلة الخاصة بي هي بعض أنواع:

static MyClass *gInstance = NULL;

+ (MyClass *)instance
{
    @synchronized(self)
    {
        if (gInstance == NULL)
            gInstance = [[self alloc] init];
    }

    return(gInstance);
}

ما الذي يمكنني القيام به لتحسين هذا؟




لتوسيع المثال من @ robbie-hanson ...

static MySingleton* sharedSingleton = nil;

+ (void)initialize {
    static BOOL initialized = NO;
    if (!initialized) {
        initialized = YES;
        sharedSingleton = [[self alloc] init];
    }
}

- (id)init {
    self = [super init];
    if (self) {
        // Member initialization here.
    }
    return self;
}



لقد تدحرجت المفرد إلى فئة ، بحيث يمكن لطبقات أخرى أن ترث خصائص فردية.

Singleton.h:

static id sharedInstance = nil;

#define DEFINE_SHARED_INSTANCE + (id) sharedInstance {  return [self sharedInstance:&sharedInstance]; } \
                               + (id) allocWithZone:(NSZone *)zone { return [self allocWithZone:zone forInstance:&sharedInstance]; }

@interface Singleton : NSObject {

}

+ (id) sharedInstance;
+ (id) sharedInstance:(id*)inst;

+ (id) allocWithZone:(NSZone *)zone forInstance:(id*)inst;

@end

Singleton.m:

#import "Singleton.h"


@implementation Singleton


+ (id) sharedInstance { 
    return [self sharedInstance:&sharedInstance];
}

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

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

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

- (id)retain {
    return self;
}

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

- (void)release {
    //do nothing
}

- (id)autorelease {
    return self;
}


@end

وهنا مثال على بعض الصف ، التي تريد أن تصبح مفرداً.

#import "Singleton.h"

@interface SomeClass : Singleton {

}

@end

@implementation SomeClass 

DEFINE_SHARED_INSTANCE;

@end

القيد الوحيد حول صنف Singleton ، هو أنه صنف NSObject. ولكن في معظم الوقت ، أستخدم الأحرف المفردة في الكود ، فهي في الواقع فئة فرعية من NSObject ، لذا فإن هذا الفصل يريح حياتي ويجعل نظافة الرمز.




لدي اختلاف مثير للاهتمام على 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 origSel = @selector(sharedInstance);
            SEL newSel = @selector(simpleSharedInstance);
            Method origMethod = class_getClassMethod(self, origSel);
            Method newMethod = class_getClassMethod(self, newSel);
            method_exchangeImplementations(origMethod, newMethod);
        }
    }
    return (MySingleton *)sharedInstance;
}



KLSingleton هو:

  1. Subclassible (إلى الدرجة n-th)
  2. متوافق مع ARC
  3. آمن مع alloc و init
  4. محملة بالكسل
  5. ذات ألوان
  6. بدون قفل (يستخدم + تهيئة ، وليسsynchronize)
  7. مجانا الكلي
  8. مجانا أفرط-
  9. بسيط

KLSingleton




أردت فقط ترك هذا هنا حتى لا أفقده. ميزة هذا واحد هو أنه قابل للاستخدام في InterfaceBuilder ، وهي ميزة ضخمة. هذا مأخوذ من سؤال آخر طرحته :

static Server *instance;

+ (Server *)instance { return instance; }

+ (id)hiddenAlloc
{
    return [super alloc];
}

+ (id)alloc
{
    return [[self instance] retain];
}


+ (void)initialize
{
    static BOOL initialized = NO;
    if(!initialized)
    {
        initialized = YES;
        instance = [[Server hiddenAlloc] init];
    }
}

- (id) init
{
    if (instance)
        return self;
    self = [super init];
    if (self != nil) {
        // whatever
    }
    return self;
}



منذ أن نشر كيندال سلسة فردية من سلسلة "سلايدز" التي تحاول تجنب تأمين التكاليف ، ظننت أنني سأقوم بإعداد واحدة أيضًا:

#import <libkern/OSAtomic.h>

static void * volatile sharedInstance = nil;                                                

+ (className *) sharedInstance {                                                                    
  while (!sharedInstance) {                                                                          
    className *temp = [[self alloc] init];                                                                 
    if(!OSAtomicCompareAndSwapPtrBarrier(0x0, temp, &sharedInstance)) {
      [temp release];                                                                                   
    }                                                                                                    
  }                                                                                                        
  return sharedInstance;                                                                        
}

حسناً ، دعني أشرح كيف يعمل هذا:

  1. الحالة السريعة: في التنفيذ العادي ، تم بالفعل تعيين sharedInstance ، لذلك لا يتم تنفيذ الحلقة while ، وتعود الدالة بعد مجرد اختبار وجود المتغير ؛

  2. الحالة البطيئة: في حالة عدم وجود sharedInstance ، يتم تخصيص مثيل sharedInstance باستخدام مقارنة And Swap ('CAS') ؛

  3. الحالة sharedInstance : إذا كان هناك نوعان من sharedInstance الاتصال بـ sharedInstance في نفس الوقت و sharedInstance لا sharedInstance في نفس الوقت ، sharedInstance كلاهما على تهيئة حالات جديدة من المفرد ومحاولة وضع CAS في موضعه. أيًا كان الفائز يفوز بالـ CAS على الفور ، أيهما يفقد الإصدار الذي خصصه للتو ، ويعيد sharedInstance (التي تم ضبطها الآن). يعمل OSAtomicCompareAndSwapPtrBarrier واحد OSAtomicCompareAndSwapPtrBarrier كتابة لمؤشر ترابط الإعداد وحاجز قراءة من مؤشر ترابط الاختبار.




لم أقرأ كل الحلول ، لذلك أغفر إذا كان هذا الرمز زائدا عن الحاجة.

هذا هو أكثر تنفيذ آمن الصفحات في رأيي.

+(SingletonObject *) sharedManager
{
    static SingletonObject * sharedResourcesObj = nil;

    @synchronized(self)
    {
        if (!sharedResourcesObj)
        {
            sharedResourcesObj = [[SingletonObject alloc] init];
        }
    }

    return sharedResourcesObj;
}



كيف حول

static MyClass *gInstance = NULL;

+ (MyClass *)instance
{
    if (gInstance == NULL) {
        @synchronized(self)
        {
            if (gInstance == NULL)
                gInstance = [[self alloc] init];
        }
    }

    return(gInstance);
}

لذلك يمكنك تجنب تكلفة التزامن بعد التهيئة؟




The accepted answer, although it compiles, is incorrect.

+ (MySingleton*)sharedInstance
{
    @synchronized(self)  <-------- self does not exist at class scope
    {
        if (sharedInstance == nil)
            sharedInstance = [[MySingleton alloc] init];
    }
    return sharedInstance;
}

Per Apple documentation:

... You can take a similar approach to synchronize the class methods of the associated class, using the Class object instead of self.

Even if using self works, it shouldn't and this looks like a copy and paste mistake to me. The correct implementation for a class factory method would be:

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



Shouln هذا يكون threadsafe وتجنب تأمين باهظة الثمن بعد المكالمة الأولى؟

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



أعلم أن هناك الكثير من التعليقات على هذا "السؤال" ، لكنني لا أرى الكثير من الناس يقترحون استخدام الماكرو لتحديد المفرد. إنه مثل هذا النمط الشائع والماكرو يبسط إلى حد كبير المفرد.

هنا هي وحدات الماكرو كتبت استناداً إلى العديد من تطبيقات Objc رأيت.

Singeton.h

/**
 @abstract  Helps define the interface of a singleton.
 @param  TYPE  The type of this singleton.
 @param  NAME  The name of the singleton accessor.  Must match the name used in the implementation.
 @discussion
 Typcially the NAME is something like 'sharedThing' where 'Thing' is the prefix-removed type name of the class.
 */
#define SingletonInterface(TYPE, NAME) \
+ (TYPE *)NAME;


/**
 @abstract  Helps define the implementation of a singleton.
 @param  TYPE  The type of this singleton.
 @param  NAME  The name of the singleton accessor.  Must match the name used in the interface.
 @discussion
 Typcially the NAME is something like 'sharedThing' where 'Thing' is the prefix-removed type name of the class.
 */
#define SingletonImplementation(TYPE, NAME) \
static TYPE *__ ## NAME; \
\
\
+ (void)initialize \
{ \
    static BOOL initialized = NO; \
    if(!initialized) \
    { \
        initialized = YES; \
        __ ## NAME = [[TYPE alloc] init]; \
    } \
} \
\
\
+ (TYPE *)NAME \
{ \
    return __ ## NAME; \
}

مثال على الاستخدام:

MyManager.h

@interface MyManager

SingletonInterface(MyManager, sharedManager);

// ...

@end

MyManager.m

@implementation MyManager

- (id)init
{
    self = [super init];
    if (self) {
        // Initialization code here.
    }

    return self;
}

SingletonImplementation(MyManager, sharedManager);

// ...

@end

لماذا ماكرو الواجهة عندما يكون فارغًا تقريبًا؟ تناسق التعليمة البرمجية بين رأس وملفات التعليمات البرمجية؛ الصيانة في حال كنت ترغب في إضافة المزيد من الطرق الآلية أو تغييرها.

أستخدم طريقة التهيئة لإنشاء المفرد كما هو مستخدم في الإجابة الأكثر شيوعًا هنا (وقت كتابة هذا التقرير).




تحرير: هذا التنفيذ عفا عليه الزمن مع ARC. يرجى إلقاء نظرة على كيف يمكنني تنفيذ singleton-C الهدف الذي يتوافق مع ARC؟ للتنفيذ الصحيح.

جميع تطبيقات التهيئة التي قرأتها في إجابات أخرى تشترك في خطأ شائع.

+ (void) initialize {
  _instance = [[MySingletonClass alloc] init] // <----- Wrong!
}

+ (void) initialize {
  if (self == [MySingletonClass class]){ // <----- Correct!
      _instance = [[MySingletonClass alloc] init] 
  }
}

توصي وثائق Apple بالتحقق من نوع الفئة في كتلة التهيئة الخاصة بك. نظرًا لأن الفئة الفرعية تستدعي التهيئة افتراضيًا. توجد حالة غير واضحة حيث يمكن إنشاء الفئات الفرعية بشكل غير مباشر من خلال KVO. إذا قمت بإضافة السطر التالي في فئة أخرى:

[[MySingletonClass getInstance] addObserver:self forKeyPath:@"foo" options:0 context:nil]

الهدف C- سيؤدي ضمنيًا إلى إنشاء فئة فرعية من MySingletonClass مما يؤدي إلى تحريك ثاني لـ +initialize .

قد تعتقد أنه يجب عليك التحقق بشكل ضمني من التهيئة المكررة في كتلة init كما يلي:

- (id) init { <----- Wrong!
   if (_instance != nil) {
      // Some hack
   }
   else {
      // Do stuff
   }
  return self;
}

لكنك ستطلق النار على نفسك في القدم. أو أسوأ يعطي مطور آخر فرصة لاطلاق النار على أنفسهم في القدم.

- (id) init { <----- Correct!
   NSAssert(_instance == nil, @"Duplication initialization of singleton");
   self = [super init];
   if (self){
      // Do stuff
   }
   return self;
}

TL ؛ DR ، وهنا التنفيذ

@implementation MySingletonClass
static MySingletonClass * _instance;
+ (void) initialize {
   if (self == [MySingletonClass class]){
      _instance = [[MySingletonClass alloc] init];
   }
}

- (id) init {
   ZAssert (_instance == nil, @"Duplication initialization of singleton");
   self = [super init];
   if (self) {
      // Initialization
   }
   return self;
}

+ (id) getInstance {
   return _instance;
}
@end

(استبدل ZAssert بماكرو التوكيد الخاص بنا ، أو مجرد NSAssert.)






Related