333

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

static MyClass *gInstance = NULL;

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

    return(gInstance);
}

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

4

26 回答 26

207

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

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

所以你可以做类似这样的事情:

static MySingleton *sharedSingleton;

+ (void)initialize
{
    static BOOL initialized = NO;
    if(!initialized)
    {
        initialized = YES;
        sharedSingleton = [[MySingleton alloc] init];
    }
}
于 2008-12-05T08:24:30.407 回答
95
@interface MySingleton : NSObject
{
}

+ (MySingleton *)sharedSingleton;
@end

@implementation MySingleton

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

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

    return sharedSingleton;
  }
}

@end

[来源]

于 2008-09-28T03:46:15.257 回答
59

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

+ (id)sharedFoo
{
    static dispatch_once_t once;
    static MyFoo *sharedFoo;
    dispatch_once(&once, ^ { sharedFoo = [[self alloc] init]; });
    return sharedFoo;
}
于 2008-09-28T07:05:14.917 回答
58

由于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;                                                                        
}

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

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

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

  3. 有争议的情况:如果两个线程都尝试同时调用并且sharedInstance同时不存在,那么它们都将初始化单例的新实例并尝试将其 CAS 放置到位。无论谁赢了 CAS 都会立即返回,无论谁输了都会释放它刚刚分配的实例并返回 (now set) 。单个充当设置线程的写屏障和测试线程的读屏障。 sharedInstancesharedInstanceOSAtomicCompareAndSwapPtrBarrier

于 2010-03-15T18:56:31.293 回答
14
静态 MyClass *sharedInst = nil;

+ (id)sharedInstance
{
    @同步(自我){
        if ( sharedInst == nil ) {
            /* sharedInst 在 init 中设置 */
            [[自分配] 初始化];
        }
    }
    返回sharedInst;
}

- (id)初始化
{
    如果(sharedInst!= nil){
        [NSException 引发:NSInternalInconsistencyException
            格式:@"[%@ %@] 不能被调用;使用 +[%@ %@] 代替"],
            NSStringFromClass([self class]), NSStringFromSelector(_cmd),
            NSStringFromClass([self class]),
            NSStringFromSelector(@selector(sharedInstance)"];
    } else if ( self = [super init] ) {
        sharedInst = 自我;
        /* 这里具体的类 */
    }
    返回sharedInst;
}

/* 这些可能在
   GC 应用程序。保持单身
   作为一个实际的单身人士
   非CG应用
*/
- (NSUInteger)retainCount
{
    返回 NSUIntegerMax;
}

- (oneway void)release
{
}

- (id)保留
{
    返回sharedInst;
}

- (id)自动释放
{
    返回sharedInst;
}
于 2008-09-28T04:34:43.423 回答
12

编辑:此实现已被 ARC 淘汰。请看一下如何实现与 ARC 兼容的 Objective-C 单例?为正确实施。

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

+ (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]

Objective-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。)

于 2011-06-07T20:14:59.147 回答
10

Singleton 宏代码的详尽解释在博客 Cocoa With Love

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

于 2009-06-23T12:01:58.167 回答
9

我对 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;
}
于 2010-02-19T06:15:13.057 回答
6

简短的回答:太棒了。

长答案:像......

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

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

于 2012-02-08T03:12:32.100 回答
5

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

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

单例.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 的子类,所以这个类真的让我的生活变得轻松,让代码更干净。

于 2010-11-30T10:00:44.253 回答
2

这也适用于非垃圾收集环境。

@interface MySingleton : NSObject {
}

+(MySingleton *)sharedManager;

@end


@implementation MySingleton

static MySingleton *sharedMySingleton = nil;

+(MySingleton*)sharedManager {
    @synchronized(self) {
        if (sharedMySingleton == nil) {
            [[self alloc] init]; // assignment not done here
        }
    }
    return sharedMySingleton;
}


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


-(void)dealloc {
    [super dealloc];
}

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


-(id)retain {
    return self;
}


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


-(void)release {
    //do nothing    
}


-(id)autorelease {
    return self;    
}


-(id)init {
    self = [super init];
    sharedMySingleton = self;

    //initialize here

    return self;
}

@end
于 2008-09-28T04:58:01.340 回答
2

这是我放在一起的一个宏

http://github.com/cjhanson/Objective-C-Optimized-Singleton

它基于Matt Gallagher 的工作, 但将实现更改为使用方法 swizzling,如 Google 的 Dave MacLachlan 所述

我欢迎评论/贡献。

于 2010-03-02T06:26:11.253 回答
2

这不应该是线程安全的并避免第一次调用后昂贵的锁定吗?

+ (MySingleton*)sharedInstance
{
    if (sharedInstance == nil) {
        @synchronized(self) {
            if (sharedInstance == nil) {
                sharedInstance = [[MySingleton alloc] init];
            }
        }
    }
    return (MySingleton *)sharedInstance;
}
于 2010-03-17T11:13:55.320 回答
2

有关 Objective-C 中单例模式的深入讨论,请看这里:

在 Objective-C 中使用单例模式

于 2010-05-21T22:39:30.867 回答
2

怎么样

static MyClass *gInstance = NULL;

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

    return(gInstance);
}

所以你避免初始化后的同步成本?

于 2011-12-29T18:32:32.070 回答
1

KLSingleton 是:

  1. 可子分类(到第 n 级)
  2. ARC兼容
  3. 安全allocinit
  4. 懒加载
  5. 线程安全
  6. 无锁(使用 +initialize,而不是 @synchronize)
  7. 无微距
  8. 无调酒
  9. 简单的

KLS单顿

于 2012-05-03T21:50:27.190 回答
0

你不想在 self 上同步......因为 self 对象还不存在!您最终锁定了一个临时 id 值。您要确保没有其他人可以运行类方法( sharedInstance、alloc、allocWithZone: 等),因此您需要在类对象上进行同步:

@implementation MYSingleton

static MYSingleton * sharedInstance = nil;

+( id )sharedInstance {
    @synchronized( [ MYSingleton class ] ) {
        if( sharedInstance == nil )
            sharedInstance = [ [ MYSingleton alloc ] init ];
    }

    return sharedInstance;
}

+( id )allocWithZone:( NSZone * )zone {
    @synchronized( [ MYSingleton class ] ) {
        if( sharedInstance == nil )
            sharedInstance = [ super allocWithZone:zone ];
    }

    return sharedInstance;
}

-( id )init {
    @synchronized( [ MYSingleton class ] ) {
        self = [ super init ];
        if( self != nil ) {
            // Insert initialization code here
        }

        return self;
    }
}

@end
于 2010-01-13T22:01:52.067 回答
0
static mySingleton *obj=nil;

@implementation mySingleton

-(id) init {
    if(obj != nil){     
        [self release];
        return obj;
    } else if(self = [super init]) {
        obj = self;
    }   
    return obj;
}

+(mySingleton*) getSharedInstance {
    @synchronized(self){
        if(obj == nil) {
            obj = [[mySingleton alloc] init];
        }
    }
    return obj;
}

- (id)retain {
    return self;
}

- (id)copy {
    return self;
}

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

- (void)release {
    if(obj != self){
        [super release];
    }
    //do nothing
}

- (id)autorelease {
    return self;
}

-(void) dealloc {
    [super dealloc];
}
@end
于 2010-06-18T10:22:44.947 回答
0

只是想把它留在这里,这样我就不会丢失它。这个的优点是它可以在 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;
}
于 2011-01-16T23:29:34.710 回答
0

我知道对这个“问题”有很多评论,但我没有看到很多人建议使用宏来定义单例。这是一种常见的模式,宏大大简化了单例。

以下是我根据我见过的几个 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

为什么接口宏几乎是空的?头文件和代码文件之间的代码一致性;可维护性,以防您想添加更多自动方法或更改它。

我正在使用初始化方法来创建单例,就像这里最流行的答案中使用的那样(在撰写本文时)。

于 2011-11-22T20:00:41.577 回答
0

使用 Objective C 类方法,我们可以避免以通常的方式使用单例模式,从:

[[Librarian sharedInstance] openLibrary]

到:

[Librarian openLibrary]

通过将类包装在另一个只有Class Methods的类中,这样就不会意外创建重复的实例,因为我们没有创建任何实例!

我在这里写了一个更详细的博客:)

于 2012-02-10T22:35:08.610 回答
0

从@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;
}
于 2012-03-22T16:52:31.127 回答
0

我的方法很简单:

static id instanceOfXXX = nil;

+ (id) sharedXXX
{
    static volatile BOOL initialized = NO;

    if (!initialized)
    {
        @synchronized([XXX class])
        {
            if (!initialized)
            {
                instanceOfXXX = [[XXX alloc] init];
                initialized = YES;
            }
        }
    }

    return instanceOfXXX;
}

如果单例已经初始化,则不会进入 LOCK 块。第二个检查 if(!initialized) 是确保当前线程获取 LOCK 时它尚未初始化。

于 2012-12-23T22:01:09.113 回答
0

我还没有通读所有的解决方案,所以如果这段代码是多余的,请原谅。

在我看来,这是最线程安全的实现。

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

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

    return sharedResourcesObj;
}
于 2013-02-19T03:01:07.933 回答
-4

我通常使用与 Ben Hoffstein 的回答中的代码大致相似的代码(我也从 Wikipedia 中获得)。我使用它是出于 Chris Hanson 在评论中所述的原因。

但是,有时我需要将单例放入 NIB,在这种情况下,我使用以下内容:

@implementation Singleton

static Singleton *singleton = nil;

- (id)init {
    static BOOL initialized = NO;
    if (!initialized) {
        self = [super init];
        singleton = self;
        initialized = YES;
    }
    return self;
}

+ (id)allocWithZone:(NSZone*)zone {
    @synchronized (self) {
        if (!singleton)
            singleton = [super allocWithZone:zone];     
    }
    return singleton;
}

+ (Singleton*)sharedSingleton {
    if (!singleton)
        [[Singleton alloc] init];
    return singleton;
}

@end

我将-retain(等)的实现留给读者,尽管上面的代码是你在垃圾收集环境中所需要的。

于 2009-06-24T04:53:41.767 回答
-5

接受的答案虽然可以编译,但不正确。

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

根据 Apple 文档:

...您可以采取类似的方法来同步关联类的类方法,使用Class对象而不是self。

即使使用自我工作,也不应该,这对我来说似乎是一个复制和粘贴错误。类工厂方法的正确实现是:

+ (MySingleton*)getInstance
{
    @synchronized([MySingleton class]) 
    {
        if (sharedInstance == nil)
            sharedInstance = [[MySingleton alloc] init];
    }
    return sharedInstance;
}
于 2011-01-23T23:23:44.130 回答