首先,是的,这是一个线程安全的解决方案。这种dispatch_once
模式是在Objective-C中生成单例的现代,线程安全的方式。那里不用担心。
但是,您问过,这是否是“最佳”方法。但是,应该承认instancetype
和[[self alloc] init]
与单例一起使用时可能会产生误导。
这样做的好处instancetype
是,这是一种明确的方法,可以声明该类可以被子类化,而无需诉诸于id
像去年那样进行使用。
但是static
这种方法提出了子类化的挑战。如果if ImageCache
和BlobCache
singleton都是Cache
超类的子类,而没有实现自己的sharedCache
方法,该怎么办?
ImageCache *imageCache = [ImageCache sharedCache]; // fine
BlobCache *blobCache = [BlobCache sharedCache]; // error; this will return the aforementioned ImageCache!!!
为此,您必须确保子类实现自己的子类 sharedInstance
(或为特定类调用的方法)。
最重要的是,您的原始sharedInstance
外观看起来将支持子类,但不会。如果您打算支持子类化,则至少要包含文档,以警告未来的开发人员他们必须重写此方法。
为了获得与Swift的最佳互操作性,您可能希望将其定义为属性,而不是类方法,例如:
@interface Foo : NSObject
@property (class, readonly, strong) Foo *sharedFoo;
@end
然后,您可以继续为此属性编写一个吸气剂(实现将使用dispatch_once
您建议的模式):
+ (Foo *)sharedFoo { ... }
这样做的好处是,如果Swift用户开始使用它,他们将执行以下操作:
let foo = Foo.shared
请注意,没有()
,因为我们将其实现为属性。从Swift 3开始,这就是通常访问单例的方式。因此,将其定义为属性有助于促进这种互操作性。
顺便说一句,如果您查看Apple如何定义他们的单身人士,这就是他们采用的模式,例如,他们的NSURLSession
单身人士定义如下:
@property (class, readonly, strong) NSURLSession *sharedSession;
另一个非常小的Swift互操作性考虑因素是单例的名称。最好是可以合并类型的名称,而不是sharedInstance
。例如,如果类为Foo
,则可以将singleton属性定义为sharedFoo
。或者如果是该类DatabaseManager
,则可以调用该属性sharedManager
。然后,Swift用户可以执行以下操作:
let foo = Foo.shared
let manager = DatabaseManager.shared
显然,如果您确实想使用sharedInstance
,则可以随时声明Swift名称:
@property (class, readonly, strong) Foo* sharedInstance NS_SWIFT_NAME(shared);
显然,在编写Objective-C代码时,我们不应该让Swift的互操作性超过其他设计考虑因素,但是,如果我们可以编写优雅地支持两种语言的代码,那还是更好的选择。
我同意其他人的意见,他们指出,如果您希望这是一个真正的单例,开发人员无法(不应)实例化自己的实例,则unavailable
限定符为on init
且new
谨慎。