我想拥有一个线程安全的,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中抛出异常)?
答案 0 :(得分:2)
使用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
方法,并接收共享相同状态的对象。他们甚至不需要意识到它是一个单身人士。
答案 1 :(得分:2)
您还必须覆盖+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
}
编辑#1
你不一定需要在这里抛出一个异常,但对于他们以错误的方式使用你的类的开发人员来说,这比发回一个简单的nil
指针更为直观。
编辑#2
我添加了一个简单的技巧,以避免开发人员实例化该类以绕过修改后的+alloc
方法,在这种情况下,分配将运行良好,但-init
将抛出异常。
答案 2 :(得分:1)
我倾向于使用以下内容:(使用较新的instancetype
编译器语法)
@implementation MyClass
+ (instancetype)myClass {
static MyClass *singleton; // keep global variables in the most minimal scope
if (singleton == nil) @synchronized (self) {
singleton = [[MyClass alloc] initPrivate];
}
return singleton;
}
- (instancetype)initPrivate { // ARC requires the method start with "init…"
self = [super init];
return self;
}
- (instancetype)init {
return nil;
}
@end
这也不会阻止其他人调用[[MyClass alloc] privateInit]
,但会警告他们(除非他们自己编写代码)。
如果有人打电话给[[MyClass alloc] init]
,这也会在ARC之前泄漏 - 但如果发生这种情况,你会遇到更大的问题。或者,您可以在调用init时抛出异常。 (如holex的回答)
此外,子类理论上可以处于竞争状态。如果您担心此更改@synchronized (self)
到@synchronized ([McClass class])
。我更喜欢self
作为更清晰的代码,我知道不会有子类。
答案 3 :(得分:0)
将实例转换为类方法,并使用Class对象作为单例。
例如你有一个像这样的单身类
@interface MySingleton {
int count;
}
+ (MySingleton *)sharedInstance;
- (int)getNext;
@end
我建议您将其转换为
@interface MySingleton
+ (int)getNext;
@end
在MySingleton.m
static int count;
然后你可以像
一样使用它[MySingleton getNext];
或
id obj = [MySingleton class]; // Class objects are singleton provided by runtime
[obj getNext];
修改强>
我只想指出,单例模式已经有很多ObjC实现。
http://www.cocoawithlove.com/2008/11/singletons-appdelegates-and-top-level.html https://github.com/fadingred/objc-singleton
简单的谷歌搜索会找到它们。一切都考虑在内。 (远远超出我的预期)