在 Objective-C 中使用 init 方法创建单例的安全方法
Safe way to create singleton with init method in Objective-C
下一步我想采用使用共享实例的 GCD 方法,因此我创建了以下代码:
@implementation MyClass
static id sharedInstance;
#pragma mark Initialization
+ (instancetype)sharedInstance {
static dispatch_once_t once;
dispatch_once(&once, ^{
sharedInstance = [[self alloc] init];
});
return sharedInstance;
}
- (instancetype)init {
if (sharedInstance) {
return sharedInstance;
}
@synchronized(self) {
self = [super init];
if (self) {
sharedInstance = self;
}
return self;
}
}
@end
我认为 sharedInstance
方法似乎没问题,但我不确定 init 方法。创建这个的原因是我不希望人们使用我的 SDK,使用 init 方法,如果他们这样做......让它成为防弹的。
我建议完全不允许调用 init,而不是透明地将对 init
的调用重定向到单例实现,这可能会给您的 SDK 用户带来非常混乱的行为:
+ (instancetype)sharedInstance {
static dispatch_once_t once;
dispatch_once(&once, ^{
sharedInstance = [[self alloc] initPrivate];
});
return sharedInstance;
}
- (instancetype)init {
@throw [NSException exceptionWithName:NSInternalInconsistencyException reason:@"..." userInfo:nil];
}
- (instancetype)initPrivate {
if (self = [super init]) {
...
}
return self;
}
普遍的看法是,试图保护您的单身人士免受此类错误的侵害是毫无意义的。谁调用 [[LUIMain alloc] init]
并创建一个单例,谁就得到了他们应得的。
并且您编写的代码无论如何都不是线程安全的。如果我调用 [[LUIMain alloc] init]
而其他人调用 sharedInstance,sharedInstance 将 return 与下一次调用不同的对象。 (init 方法中的 @synchronized (self)
是没有意义的,因为第二个调用者将有不同的自我)。
我想提出解决您问题的新方法。
您可以在 header 文件中使用 NS_UNAVAILABLE
,就像这样:
//Header file
@interface MyClass : NSObject
+ (instancetype)sharedInstance
- (instancetype)init NS_UNAVAILABLE;
//...
@end
在这种情况下,init
函数将无法从外部使用,不会被建议自动完成,您将能够正常使用实现文件中的 init
方法。
当你制作单例时 class 我建议你通过将此行添加到 header 文件来使 new
方法不可用:
+ (instancetype)new NS_UNAVAILABLE;
还有一种使方法不可用的老方法(也可以在 header 中使用):
- (instancetype) init __attribute__((unavailable("Use 'sharedInstance' instead of 'init' as this class is singleton.")));
如果你想提示一些关于不可用的消息,可以使用这个。
下一步我想采用使用共享实例的 GCD 方法,因此我创建了以下代码:
@implementation MyClass
static id sharedInstance;
#pragma mark Initialization
+ (instancetype)sharedInstance {
static dispatch_once_t once;
dispatch_once(&once, ^{
sharedInstance = [[self alloc] init];
});
return sharedInstance;
}
- (instancetype)init {
if (sharedInstance) {
return sharedInstance;
}
@synchronized(self) {
self = [super init];
if (self) {
sharedInstance = self;
}
return self;
}
}
@end
我认为 sharedInstance
方法似乎没问题,但我不确定 init 方法。创建这个的原因是我不希望人们使用我的 SDK,使用 init 方法,如果他们这样做......让它成为防弹的。
我建议完全不允许调用 init,而不是透明地将对 init
的调用重定向到单例实现,这可能会给您的 SDK 用户带来非常混乱的行为:
+ (instancetype)sharedInstance {
static dispatch_once_t once;
dispatch_once(&once, ^{
sharedInstance = [[self alloc] initPrivate];
});
return sharedInstance;
}
- (instancetype)init {
@throw [NSException exceptionWithName:NSInternalInconsistencyException reason:@"..." userInfo:nil];
}
- (instancetype)initPrivate {
if (self = [super init]) {
...
}
return self;
}
普遍的看法是,试图保护您的单身人士免受此类错误的侵害是毫无意义的。谁调用 [[LUIMain alloc] init]
并创建一个单例,谁就得到了他们应得的。
并且您编写的代码无论如何都不是线程安全的。如果我调用 [[LUIMain alloc] init]
而其他人调用 sharedInstance,sharedInstance 将 return 与下一次调用不同的对象。 (init 方法中的 @synchronized (self)
是没有意义的,因为第二个调用者将有不同的自我)。
我想提出解决您问题的新方法。
您可以在 header 文件中使用 NS_UNAVAILABLE
,就像这样:
//Header file
@interface MyClass : NSObject
+ (instancetype)sharedInstance
- (instancetype)init NS_UNAVAILABLE;
//...
@end
在这种情况下,init
函数将无法从外部使用,不会被建议自动完成,您将能够正常使用实现文件中的 init
方法。
当你制作单例时 class 我建议你通过将此行添加到 header 文件来使 new
方法不可用:
+ (instancetype)new NS_UNAVAILABLE;
还有一种使方法不可用的老方法(也可以在 header 中使用):
- (instancetype) init __attribute__((unavailable("Use 'sharedInstance' instead of 'init' as this class is singleton.")));
如果你想提示一些关于不可用的消息,可以使用这个。