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

时间:2020-03-06 14:50:19  来源:igfitidea点击:

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

static MyClass *gInstance = NULL;

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

    return(gInstance);
}

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

解决方案

@interface MySingleton : NSObject
{
}

+ (MySingleton *)sharedSingleton;
@end

@implementation MySingleton

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

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

    return sharedSingleton;
  }
}

@end

[来源]

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

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

@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

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

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

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

The runtime sends initialize to 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 initialize message to classes in a thread-safe manner. Superclasses receive this message before their subclasses.

因此,我们可以执行以下操作:

static MySingleton *sharedSingleton;

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

有关Singleton宏代码的完整解释,请参见博客Cocoa With Love。

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

我通常使用与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(等)的实现留给读者看。

我们不想在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

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

这是我放在一起的宏:

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

它基于马特·加拉格尔(Matt Gallagher)的工作
但是,如Google的Dave MacLachlan所述,将实现更改为使用方法混乱。

我欢迎评论/贡献。

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

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

  • 快速案例:在正常执行中,已经设置了sharedInstance,因此,while循环不会执行,函数仅在测试了变量的存在之后返回;
  • 慢速情况:如果不存在" sharedInstance",则使用"比较并交换"('CAS')分配一个实例并将其复制到该实例中;
  • 有争议的情况:如果两个线程都尝试同时调用sharedInstance,并且sharedInstance不同时存在,则它们都将初始化单例的新实例并尝试将其放置到适当位置。赢得CAS的任何一方立即返回,失去CAS的任何一方释放它刚刚分配的实例,并返回(现在设置)" sharedInstance"。单个OSAtomicCompareAndSwapPtrBarrier既充当设置线程的写屏障,也充当测试线程的读屏障。

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

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

有关Objective-C中的单例模式的深入讨论,请参见此处:

在Objective-C中使用单例模式

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

我已经将单例转换成一个类,因此其他类可以继承单例属性。

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子类,因此此类确实简化了我的生活,并使代码更加整洁。