我的单例访问方法通常是一些变种:

static MyClass *gInstance = NULL;

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

    return(gInstance);
}

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


当前回答

公认的答案,虽然它编译,是不正确的。

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

根据苹果文档:

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

即使使用self工作,它不应该,这看起来像一个复制和粘贴错误对我来说。 类工厂方法的正确实现应该是:

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

其他回答

另一个选项是使用+(void)初始化方法。从文档中可以看到:

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

所以你可以这样做:

static MySingleton *sharedSingleton;

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

扩展一下@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;
}
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;
}

编辑:这个实现在ARC中过时了。请看看我如何实现一个与ARC兼容的Objective-C单例?为了正确的执行。

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

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

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

苹果文档建议你检查初始化块中的类类型。因为子类默认调用初始化。存在一种不明显的情况,即可以通过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。)

KLSingleton是: 可子类化(到n阶) 弧相容 使用alloc和init是安全的 装载 线程安全的 无锁(使用+initialize,而不是@synchronize) Macro-free Swizzle-free 简单的

克林格尔顿