Objective-C中使用GCD的dispatch_once创建单例
如果您可以
使用 GCD 面向 iOS 4.0 或更高版本,那么这是在 Objective-C 中创建单例(线程安全)的最佳方法吗?
+ (instancetype)sharedInstance
{
static dispatch_once_t once;
static id sharedInstance;
dispatch_once(&once, ^{
sharedInstance = [[self alloc] init];
});
return sharedInstance;
}
If you can target iOS 4.0 or above
Using GCD, is it the best way to create singleton in Objective-C (thread safe)?
+ (instancetype)sharedInstance
{
static dispatch_once_t once;
static id sharedInstance;
dispatch_once(&once, ^{
sharedInstance = [[self alloc] init];
});
return sharedInstance;
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(10)
这是创建类实例的完全可接受且线程安全的方式。从技术上讲,它可能不是“单例”(因为这些对象中只能有 1 个),但只要您只使用
[Foo sharedFoo]
方法来访问该对象,这就是够好了。This is a perfectly acceptable and thread-safe way to create an instance of your class. It may not technically be a "singleton" (in that there can only ever be 1 of these objects), but as long as you only use the
[Foo sharedFoo]
method to access the object, this is good enough.instancetype
instancetype
只是Objective-C
的众多语言扩展之一,每个新版本都会添加更多语言扩展。知道它,喜欢它。
并以此为例,说明关注底层细节如何让您深入了解改造 Objective-C 的强大新方法。
请参阅此处:instancetype
instancetype
instancetype
is just one of the many language extensions toObjective-C
, with more being added with each new release.Know it, love it.
And take it as an example of how paying attention to the low-level details can give you insights into powerful new ways to transform Objective-C.
Refer here: instancetype
MySingleton.h
MySingleton.m
MySingleton.h
MySingleton.m
您可以通过覆盖 alloc 方法来避免分配该类。
You can avoid that the class be allocated with overwriting the alloc method.
戴夫是对的,那很好。您可能需要查看 Apple 关于创建单例的文档,了解实现其他一些方法的提示以确保如果类选择不使用sharedFoo方法,则只能创建一个。
Dave is correct, that is perfectly fine. You may want to check out Apple's docs on creating a singleton for tips on implementing some of the other methods to ensure that only one can ever be created if classes choose NOT to use the sharedFoo method.
如果你想确保[[MyClass alloc] init]返回与sharedInstance相同的对象(我认为没有必要,但有些人想要它),可以使用第二个dispatch_once非常轻松且安全地完成:
这允许任何[[MyClass alloc] init] 和 [MyClass sharedInstance] 的组合返回相同的对象; [MyClass sharedInstance] 会更高效一些。工作原理: [MyClass sharedInstance] 将调用 [[MyClass alloc] init] 一次。其他代码也可以调用它,任意次数。 init 的第一个调用者将执行“正常”初始化并将单例对象存储在 init 方法中。以后对 init 的任何调用都将完全忽略 alloc 返回的内容并返回相同的共享实例; alloc 的结果将被释放。
+sharedInstance 方法将像往常一样工作。如果它不是第一个调用 [[MyClass alloc] init] 的调用者,那么 init 的结果不是 alloc 调用的结果,但这没关系。
If you want to make sure that [[MyClass alloc] init] returns the same object as sharedInstance (not necessary in my opinion, but some folks want it), that can be done very easily and safely using a second dispatch_once:
This allows any combination of [[MyClass alloc] init] and [MyClass sharedInstance] to return the same object; [MyClass sharedInstance] would just be a bit more efficient. How it works: [MyClass sharedInstance] will call [[MyClass alloc] init] once. Other code could call it as well, any number of times. The first caller to init will do the "normal" initialisation and store the singleton object away in the init method. Any later calls to init will completely ignore what alloc returned and return the same sharedInstance; the result of alloc will be deallocated.
The +sharedInstance method will work as it always did. If it isn't the first caller to call [[MyClass alloc] init], then the result of init is not the result of the alloc call, but that is OK.
您问这是否是“创建单例的最佳方式”。
一些想法:
首先,是的,这是一个线程安全的解决方案。这种
dispatch_once
模式是在 Objective-C 中生成单例的现代、线程安全的方法。不用担心。不过,您问这是否是“最佳”方法。不过,应该承认,
instancetype
和[[self alloc] init]
在与单例结合使用时可能会产生误导。instancetype
的好处是,它是一种明确的方式来声明该类可以被子类化,而无需求助于id
类型,就像我们过去必须做的那样.但是此方法中的
静态
带来了子类化挑战。如果ImageCache
和BlobCache
单例都是Cache
超类的子类,而不实现自己的sharedCache
方法,该怎么办? p>为此,您必须确保子类实现自己的
sharedInstance
(或您为特定类调用的任何方法)。底线,您原来的
sharedInstance
看起来它将支持子类,但事实并非如此。如果您打算支持子类化,至少要包含警告未来开发人员必须重写此方法的文档。为了与 Swift 实现最佳互操作性,您可能希望将其定义为属性,而不是类方法,例如:
然后您可以继续为此属性编写一个 getter(实现将使用您建议的
dispatch_once
模式):这样做的好处是,如果 Swift 用户要使用它,他们会执行以下操作:
注意,没有
()
,因为我们将其实现为属性。从 Swift 3 开始,这就是通常访问单例的方式。因此将其定义为属性有助于促进互操作性。顺便说一句,如果你看看苹果是如何定义他们的单例的,这就是他们采用的模式,例如他们的
NSURLSession
单例定义如下:另一个非常小的 Swift 互操作性考虑因素是单身人士的名字。最好可以合并类型的名称,而不是
sharedInstance
。例如,如果类是Foo
,您可以将单例属性定义为sharedFoo
。或者,如果类是DatabaseManager
,您可以调用属性sharedManager
。那么 Swift 用户可以这样做:显然,如果您确实想使用
sharedInstance
,您可以随时声明 Swift 名称:显然,在编写 Objective-C 代码时,我们不应该让 Swift 的互操作性凌驾于其他设计考虑之上,但是,如果我们能够编写能够优雅地支持两种语言的代码,那就更好了。
我同意其他人的观点,他们指出,如果您希望这是一个真正的单例,而开发人员不能/不应该(意外地)实例化他们自己的实例,则
上的
和unavailable
限定符>initnew
是谨慎的。You ask whether this is the "best way to create singleton".
A few thoughts:
First, yes, this is a thread-safe solution. This
dispatch_once
pattern is the modern, thread-safe way to generate singletons in Objective-C. No worries there.You asked, though, whether this is the "best" way to do it. One should acknowledge, though, that the
instancetype
and[[self alloc] init]
is potentially misleading when used in conjunction with singletons.The benefit of
instancetype
is that it's an unambiguous way of declaring that the class can be subclassed without resorting to a type ofid
, like we had to do in yesteryear.But the
static
in this method presents subclassing challenges. What ifImageCache
andBlobCache
singletons were both subclasses from aCache
superclass without implementing their ownsharedCache
method?For this to work, you'd have to make sure subclasses implement their own
sharedInstance
(or whatever you call it for your particular class) method.Bottom line, your original
sharedInstance
looks like it will support subclasses, but it won't. If you intend to support subclassing, at the very least include documentation that warns future developers that they must override this method.For best interoperability with Swift, you probably want to define this to be a property, not a class method, e.g.:
Then you can go ahead and write a getter for this property (the implementation would use the
dispatch_once
pattern you suggested):The benefit of this is that if a Swift user goes to use it, they'd do something like:
Note, there is no
()
, because we implemented it as a property. Starting Swift 3, this is how singletons are generally accessed. So defining it as a property helps facilitate that interoperability.As an aside, if you look at how Apple is defining their singletons, this is the pattern that they've adopted, e.g. their
NSURLSession
singleton is defined as follows:Another, very minor Swift interoperability consideration was the name of the singleton. It's best if you can incorporate the name of the type, rather than
sharedInstance
. For example, if the class wasFoo
, you might define the singleton property assharedFoo
. Or if the class wasDatabaseManager
, you might call the propertysharedManager
. Then Swift users could do:Clearly, if you really want to use
sharedInstance
, you could always declare the Swift name should you want to:Clearly, when writing Objective-C code, we shouldn't let Swift interoperability outweigh other design considerations, but still, if we can write code that gracefully supports both languages, that's preferable.
I agree with others who point out that if you want this to be a true singleton where developers can’t/shouldn’t (accidentally) instantiate their own instances, the
unavailable
qualifier oninit
andnew
is prudent.要创建线程安全的单例,你可以这样做:
这个博客很好地解释了单例 objc/cocoa 中的单例< /a>
To create thread safe singleton you can do like this:
and this blog explain singleton very well singletons in objc/cocoa