我的 Objective-C 单例应该是什么样的?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/145154/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-03 20:46:34  来源:igfitidea点击:

What should my Objective-C singleton look like?

objective-cdesign-patternssingletonobject-initializers

提问by schwa

My singleton accessor method is usually some variant of:

我的单例访问器方法通常是以下的一些变体:

static MyClass *gInstance = NULL;

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

    return(gInstance);
}

What could I be doing to improve this?

我可以做些什么来改善这一点?

采纳答案by Robbie Hanson

Another option is to use the +(void)initializemethod. From the documentation:

另一种选择是使用该+(void)initialize方法。从文档:

The runtime sends initializeto each class in a program exactly one time just before the class, or any class that inherits from it, is sent its first message from within the program. (Thus the method may never be invoked if the class is not used.) The runtime sends the initializemessage to classes in a thread-safe manner. Superclasses receive this message before their subclasses.

运行initialize时恰好在该类或从它继承的任何类从程序内发送其第一条消息之前恰好一次向程序中的每个类发送。(因此,如果不使用该类,则该方法可能永远不会被调用。)运行时initialize以线程安全的方式将消息发送到类。超类在其子类之前收到此消息。

So you could do something akin to this:

所以你可以做一些类似的事情:

static MySingleton *sharedSingleton;

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

回答by Ben Hoffstein

@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]

[来源]

回答by Colin Barrett

Per my other answer below, I think you should be doing:

根据我下面的其他答案,我认为您应该这样做:

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

回答by Louis Gerbarg

Since Kendall posteda threadsafe singleton that attempts to avoid locking costs, I thought I would toss one up as well:

由于Kendall 发布了一个试图避免锁定成本的线程安全单例,我想我也可以扔一个:

#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;                                                                        
}

Okay, let me explain how this works:

好的,让我解释一下这是如何工作的:

  1. Fast case: In normal execution sharedInstancehas already been set, so the whileloop is never executed and the function returns after simply testing for the variable's existence;

  2. Slow case: If sharedInstancedoesn't exist, then an instance is allocated and copied into it using a Compare And Swap ('CAS');

  3. Contended case: If two threads both attempt to call sharedInstanceat the same time ANDsharedInstancedoesn't exist at the same time then they will both initialize new instances of the singleton and attempt to CAS it into position. Whichever one wins the CAS returns immediately, whichever one loses releases the instance it just allocated and returns the (now set) sharedInstance. The single OSAtomicCompareAndSwapPtrBarrieracts as both a write barrier for the setting thread and a read barrier from the testing thread.

  1. 快速情况:在正常执行时sharedInstance已经设置,所以while循环永远不会执行,函数在简单地测试变量是否存在后返回;

  2. 慢速情况:如果sharedInstance不存在,则使用比较和交换('CAS')分配一个实例并将其复制到其中;

  3. 争鸣情况:如果两个线程都试图打电话给sharedInstance在同一时间sharedInstance同一时间不存在,那么他们将单身的都初始化新实例,并尝试CAS放到正确位置。CAS 获胜者立即返回,失败者释放其刚刚分配的实例并返回 (now set) sharedInstance。singleOSAtomicCompareAndSwapPtrBarrier既是设置线程的写屏障,也是测试线程的读屏障。

回答by Louis Gerbarg

static MyClass *sharedInst = nil;

+ (id)sharedInstance
{
    @synchronize( self ) {
        if ( sharedInst == nil ) {
            /* sharedInst set up in init */
            [[self alloc] init];
        }
    }
    return sharedInst;
}

- (id)init
{
    if ( sharedInst != nil ) {
        [NSException raise:NSInternalInconsistencyException
            format:@"[%@ %@] cannot be called; use +[%@ %@] instead"],
            NSStringFromClass([self class]), NSStringFromSelector(_cmd), 
            NSStringFromClass([self class]),
            NSStringFromSelector(@selector(sharedInstance)"];
    } else if ( self = [super init] ) {
        sharedInst = self;
        /* Whatever class specific here */
    }
    return sharedInst;
}

/* These probably do nothing in
   a GC app.  Keeps singleton
   as an actual singleton in a
   non CG app
*/
- (NSUInteger)retainCount
{
    return NSUIntegerMax;
}

- (oneway void)release
{
}

- (id)retain
{
    return sharedInst;
}

- (id)autorelease
{
    return sharedInst;
}

回答by lorean

Edit: This implementation obsoleted with ARC. Please have a look at How do I implement an Objective-C singleton that is compatible with ARC?for correct implementation.

编辑:此实现已被 ARC 淘汰。请查看如何实现与 ARC 兼容的 Objective-C 单例?以便正确执行。

All the implementations of initialize I've read in other answers share a common error.

我在其他答案中读到的所有 initialize 实现都有一个常见错误。

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

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

The Apple documentation recommend you check the class type in your initialize block. Because subclasses call the initialize by default. There exists a non-obvious case where subclasses may be created indirectly through KVO. For if you add the following line in another class:

Apple 文档建议您检查 initialize 块中的类类型。因为子类默认调用初始化。存在一种不明显的情况,即可以通过 KVO 间接创建子类。如果您在另一个类中添加以下行:

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

Objective-C will implicitly create a subclass of MySingletonClass resulting in a second triggering of +initialize.

Objective-C 将隐式创建 MySingletonClass 的子类,从而导致+initialize.

You may think that you should implicitly check for duplicate initialization in your init block as such:

你可能认为你应该在你的 init 块中隐式地检查重复的初始化:

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

But you will shoot yourself in the foot; or worse give another developer the opportunity to shoot themselves in the foot.

但是你会用脚射击自己;或者更糟的是让另一个开发人员有机会用脚射击自己。

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

TL;DR, here's my implementation

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

(Replace ZAssert with our own assertion macro; or just NSAssert.)

(用我们自己的断言宏替换 ZAssert;或者只是 NSAssert。)

回答by Matthieu Cormier

A thorough explanation of the Singleton macro code is on the blog Cocoa With Love

博客 Cocoa With Love 上对 Singleton 宏代码进行了详尽的解释

http://cocoawithlove.com/2008/11/singletons-appdelegates-and-top-level.html.

http://cocoawithlove.com/2008/11/singletons-appdelegates-and-top-level.html

回答by Kendall Helmstetter Gelner

I have an interesting variation on sharedInstance that is thread safe, but does not lock after the initialization. I am not yet sure enough of it to modify the top answer as requested, but I present it for further discussion:

我对 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;
}

回答by quellish

Short answer: Fabulous.

简短的回答:太棒了。

Long answer: Something like....

长答案:有点像......

static SomeSingleton *instance = NULL;

@implementation SomeSingleton

+ (id) instance {
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        if (instance == NULL){
            instance = [[super allocWithZone:NULL] init];
        }
    });
    return instance;
}

+ (id) allocWithZone:(NSZone *)paramZone {
    return [[self instance] retain];
}

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

- (id) autorelease {
    return self;
}

- (NSUInteger) retainCount {
    return NSUIntegerMax;
}

- (id) retain {
    return self;
}

@end

Be sure to read the dispatch/once.h headerto understand what's going on. In this case the header comments are more applicable than the docs or man page.

请务必阅读dispatch/once.h 标头以了解发生了什么。在这种情况下,标题注释比文档或手册页更适用。

回答by obscenum

I've rolled singleton into a class, so other classes can inherit singleton properties.

我已将单例滚动到一个类中,因此其他类可以继承单例属性。

Singleton.h :

单例.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 :

单例.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

And here is an example of some class, that you want to become singleton.

这是一个你想成为单身人士的类的例子。

#import "Singleton.h"

@interface SomeClass : Singleton {

}

@end

@implementation SomeClass 

DEFINE_SHARED_INSTANCE;

@end

The only limitation about Singleton class, is that it is NSObject subclass. But most time I use singletons in my code they are in fact NSObject subclasses, so this class really ease my life and make code cleaner.

Singleton 类的唯一限制是它是 NSObject 子类。但大多数时候我在我的代码中使用单例,它们实际上是 NSObject 的子类,所以这个类真的让我的生活变得轻松并使代码更清晰。