Objective-C中的单例,与ARC和线程安全兼容

Objective-C中的单例,与ARC和线程安全兼容,objective-c,singleton,automatic-ref-counting,Objective C,Singleton,Automatic Ref Counting,我希望有一个线程安全、ARC兼容的单例,但在我看来,我发现的最常见的单例示例,粘贴在这里的示例: + (MyClass *)sharedInstance { static MyClass *sharedInstance = nil; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ sharedInstance = [[self alloc] init];

我希望有一个线程安全、ARC兼容的单例,但在我看来,我发现的最常见的单例示例,粘贴在这里的示例:

+ (MyClass *)sharedInstance
{
    static MyClass *sharedInstance = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        sharedInstance = [[self alloc] init];
        // Do any other initialisation stuff here
    });
    return sharedInstance;
}
不会阻止其他开发人员调用[[MyClass alloc]init]并重写所需的流。
处理它的正确方法是什么(除了在init中抛出异常)?

使用Borg模式而不是Singleton模式:允许类的多个实例化,并使实例共享相同的静态

// Shared data
static NSDictionary *sharedData = nil;

+ (void) initialize {
  // Initialize shared data
  sharedData = [[NSDictionary alloc] init];
}

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

  if (self) {
    self.data = sharedData;
  }
}

这样,客户端可以任意使用静态
getInstance
方法或
init
方法,并接收共享相同状态的对象。他们甚至不需要知道它是一个单例。

使用博格模式而不是单例模式:允许类的多个实例化,并让实例共享相同的静态

// Shared data
static NSDictionary *sharedData = nil;

+ (void) initialize {
  // Initialize shared data
  sharedData = [[NSDictionary alloc] init];
}

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

  if (self) {
    self.data = sharedData;
  }
}

这样,客户端可以任意使用静态
getInstance
方法或
init
方法,并接收共享相同状态的对象。他们甚至不需要知道它是一个单例。

您还必须重写
+alloc
方法,以避免分配多个单例实例

编辑#3:好吧,我真的知道官方文档中关于覆盖
+alloc
方法的说法,但是要实现所要求的好处,没有办法避免它。就个人而言,我不同意这样做,但它可以提供预期的结果

是这样的:

static MyClass *_sharedInstance = nil;
static BOOL _bypassAllocMethod = TRUE;

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

+ (id)alloc {
    @synchronized([MyClass class]) {
         _bypassAllocMethod = FALSE; // EDIT #2
         if (_sharedInstance == nil) {
              _sharedInstance = [super alloc];
              return _sharedInstance;
         } else {
              // EDIT #1 : you could throw an exception here to avoid the double allocation of the singleton class
              @throw [NSException exceptionWithName:[NSString stringWithFormat:@"<%@: %p> Double allocation issue", [_sharedInstance class], _sharedInstance] reason:@"You cannot allocate the singeton class twice or more." userInfo:nil];
         }
    }
    return nil;
}

// EDIT #2 : the init method
- (id)init {
    if (_bypassAllocMethod)
        @throw [NSException exceptionWithName:@"invalid allocation" reason:@"invalid allocation" userInfo:nil];

    if (self = [super init]) {
    }

    return self
}
静态MyClass*\u sharedInstance=nil;
静态布尔_bypassAllocMethod=TRUE;
+(id)共享状态{
@已同步([MyClass]){
如果(_sharedInstance==nil){
_sharedInstance=[[MyClass alloc]init];
}
}
返回-共享状态;
}
+(id)alloc{
@已同步([MyClass]){
_bypassAllocMethod=FALSE;//编辑#2
如果(_sharedInstance==nil){
_sharedInstance=[super alloc];
返回-共享状态;
}否则{
//编辑#1:您可以在这里抛出一个异常,以避免singleton类的双重分配
@抛出[NSException Exception WithName:[NSString stringWithFormat:@“双重分配问题”,[U sharedInstance类],[U sharedInstance]原因:@“您不能分配Sington类两次或更多次。”用户信息:nil];
}
}
返回零;
}
//编辑#2:init方法
-(id)init{
如果(_bypassAllocMethod)
@抛出[NSException Exception With Name:@“无效分配”原因:@“无效分配”用户信息:nil];
if(self=[super init]){
}
回归自我
}

编辑#1

您不一定需要在这里抛出异常,但对于以错误方式使用您的类的开发人员来说,这比发回一个简单的
nil
指针更直观

编辑#2


我添加了一个简单的技巧,以避免开发人员实例化类以绕过修改的
+alloc
方法,在这种情况下,分配将正常工作,但
-init
将引发异常。

您还必须重写
+alloc
方法,以避免分配多个单例实例

编辑#3:好吧,我真的知道官方文档中关于覆盖
+alloc
方法的说法,但是要实现所要求的好处,没有办法避免它。就个人而言,我不同意这样做,但它可以提供预期的结果

是这样的:

static MyClass *_sharedInstance = nil;
static BOOL _bypassAllocMethod = TRUE;

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

+ (id)alloc {
    @synchronized([MyClass class]) {
         _bypassAllocMethod = FALSE; // EDIT #2
         if (_sharedInstance == nil) {
              _sharedInstance = [super alloc];
              return _sharedInstance;
         } else {
              // EDIT #1 : you could throw an exception here to avoid the double allocation of the singleton class
              @throw [NSException exceptionWithName:[NSString stringWithFormat:@"<%@: %p> Double allocation issue", [_sharedInstance class], _sharedInstance] reason:@"You cannot allocate the singeton class twice or more." userInfo:nil];
         }
    }
    return nil;
}

// EDIT #2 : the init method
- (id)init {
    if (_bypassAllocMethod)
        @throw [NSException exceptionWithName:@"invalid allocation" reason:@"invalid allocation" userInfo:nil];

    if (self = [super init]) {
    }

    return self
}
静态MyClass*\u sharedInstance=nil;
静态布尔_bypassAllocMethod=TRUE;
+(id)共享状态{
@已同步([MyClass]){
如果(_sharedInstance==nil){
_sharedInstance=[[MyClass alloc]init];
}
}
返回-共享状态;
}
+(id)alloc{
@已同步([MyClass]){
_bypassAllocMethod=FALSE;//编辑#2
如果(_sharedInstance==nil){
_sharedInstance=[super alloc];
返回-共享状态;
}否则{
//编辑#1:您可以在这里抛出一个异常,以避免singleton类的双重分配
@抛出[NSException Exception WithName:[NSString stringWithFormat:@“双重分配问题”,[U sharedInstance类],[U sharedInstance]原因:@“您不能分配Sington类两次或更多次。”用户信息:nil];
}
}
返回零;
}
//编辑#2:init方法
-(id)init{
如果(_bypassAllocMethod)
@抛出[NSException Exception With Name:@“无效分配”原因:@“无效分配”用户信息:nil];
if(self=[super init]){
}
回归自我
}

编辑#1

您不一定需要在这里抛出异常,但对于以错误方式使用您的类的开发人员来说,这比发回一个简单的
nil
指针更直观

编辑#2


我添加了一个简单的技巧,以避免开发人员实例化类以绕过修改的
+alloc
方法,在这种情况下,分配将正常工作,但
-init
将引发异常。

将实例转换为类方法,并将类对象用作单例

例如,您有一个这样的单例类

@interface MySingleton {
    int count;
}

+ (MySingleton *)sharedInstance;
- (int)getNext;

@end
我建议你把它改成

@interface MySingleton

+ (int)getNext;

@end
在迈辛格尔顿

static int count;
然后你可以像这样使用它

[MySingleton getNext];


编辑

我只想指出,对于单例模式已经有很多ObjC实现了


简单的谷歌搜索就能找到它们。一切都考虑到了。(比我预期的要多得多)

将实例转换为类方法,并使用类对象作为单例

例如,您有一个这样的单例类

@interface MySingleton {
    int count;
}

+ (MySingleton *)sharedInstance;
- (int)getNext;

@end
我建议你改变一下