ios 在 Objective-C 中使用 GCD 的 dispatch_once 创建单例
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/5720029/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Create singleton using GCD's dispatch_once in Objective-C
提问by Ryan
If you can target iOS 4.0 or above
如果您可以针对 iOS 4.0 或更高版本
Using GCD, is it the best way to create singleton in Objective-C (thread safe)?
使用 GCD,它是在 Objective-C(线程安全)中创建单例的最佳方式吗?
+ (instancetype)sharedInstance
{
static dispatch_once_t once;
static id sharedInstance;
dispatch_once(&once, ^{
sharedInstance = [[self alloc] init];
});
return sharedInstance;
}
采纳答案by Dave DeLong
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.
这是创建类实例的一种完全可以接受且线程安全的方式。从技术上讲,它可能不是“单例”(因为这些对象只能有 1 个),但只要您只使用该[Foo sharedFoo]
方法访问该对象,这就足够了。
回答by Zelko
instancetype
实例类型
instancetype
is just one of the many language extensions to Objective-C
, with more being added with each new release.
instancetype
只是 的众多语言扩展之一,Objective-C
每个新版本都会添加更多语言扩展。
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.
并以此为例说明关注底层细节如何让您深入了解转换 Objective-C 的强大新方法。
+ (instancetype)sharedInstance
{
static dispatch_once_t once;
static id sharedInstance;
dispatch_once(&once, ^
{
sharedInstance = [self new];
});
return sharedInstance;
}
+ (Class*)sharedInstance
{
static dispatch_once_t once;
static Class *sharedInstance;
dispatch_once(&once, ^
{
sharedInstance = [self new];
});
return sharedInstance;
}
回答by Sergey Petruk
MySingleton.h
MySingleton.h
@interface MySingleton : NSObject
+(instancetype)sharedInstance;
+(instancetype)alloc __attribute__((unavailable("alloc not available, call sharedInstance instead")));
-(instancetype)init __attribute__((unavailable("init not available, call sharedInstance instead")));
+(instancetype)new __attribute__((unavailable("new not available, call sharedInstance instead")));
-(instancetype)copy __attribute__((unavailable("copy not available, call sharedInstance instead")));
@end
MySingleton.m
MySingleton.m
@implementation MySingleton
+(instancetype)sharedInstance {
static dispatch_once_t pred;
static id shared = nil;
dispatch_once(&pred, ^{
shared = [[super alloc] initUniqueInstance];
});
return shared;
}
-(instancetype)initUniqueInstance {
return [super init];
}
@end
回答by i-developer
You can avoid that the class be allocated with overwriting the alloc method.
您可以通过覆盖 alloc 方法来避免分配类。
@implementation MyClass
static BOOL useinside = NO;
static id _sharedObject = nil;
+(id) alloc {
if (!useinside) {
@throw [NSException exceptionWithName:@"Singleton Vialotaion" reason:@"You are violating the singleton class usage. Please call +sharedInstance method" userInfo:nil];
}
else {
return [super alloc];
}
}
+(id)sharedInstance
{
static dispatch_once_t p = 0;
dispatch_once(&p, ^{
useinside = YES;
_sharedObject = [[MyClass alloc] init];
useinside = NO;
});
// returns the same object each time
return _sharedObject;
}
回答by Christian
Dave is correct, that is perfectly fine. You may want to check out Apple's docs on creating a singletonfor 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.
戴夫是对的,这完全没问题。您可能需要查看Apple 的有关创建单例的文档,以获取有关实现其他一些方法的提示,以确保如果类选择不使用 sharedFoo 方法,则只能创建一个。
回答by gnasher729
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:
如果您想确保 [[MyClass alloc] init] 返回与 sharedInstance 相同的对象(在我看来不是必需的,但有些人想要它),可以使用第二个 dispatch_once 轻松安全地完成:
- (instancetype)init
{
static dispatch_once_t once;
static Class *sharedInstance;
dispatch_once(&once, ^
{
// Your normal init code goes here.
sharedInstance = self;
});
return sharedInstance;
}
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.
这允许 [[MyClass alloc] init] 和 [MyClass sharedInstance] 的任意组合返回相同的对象;[MyClass sharedInstance] 会更高效一点。工作原理: [MyClass sharedInstance] 将调用 [[MyClass alloc] init] 一次。其他代码也可以多次调用它。init 的第一个调用者将执行“正常”初始化并将单例对象存储在 init 方法中。以后对 init 的任何调用都将完全忽略 alloc 返回的内容并返回相同的 sharedInstance;alloc 的结果将被释放。
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.
+sharedInstance 方法将像往常一样工作。如果不是第一个调用 [[MyClass alloc] init] 的调用者,那么 init 的结果不是 alloc 调用的结果,但没关系。
回答by Rob
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?ImageCache *imageCache = [ImageCache sharedCache]; // fine BlobCache *blobCache = [BlobCache sharedCache]; // error; this will return the aforementioned ImageCache!!!
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
lookslike 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.:
@interface Foo : NSObject @property (class, readonly, strong) Foo *sharedFoo; @end
Then you can go ahead and write a getter for this property (the implementation would use the
dispatch_once
pattern you suggested):+ (Foo *)sharedFoo { ... }
The benefit of this is that if a Swift user goes to use it, they'd do something like:
let foo = Foo.shared
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:@property (class, readonly, strong) NSURLSession *sharedSession;
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:let foo = Foo.shared let manager = DatabaseManager.shared
Clearly, if you really want to use
sharedInstance
, you could always declare the Swift name should you want to:@property (class, readonly, strong) Foo* sharedInstance NS_SWIFT_NAME(shared);
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.
首先,是的,这是一个线程安全的解决方案。这种
dispatch_once
模式是在 Objective-C 中生成单例的现代、线程安全的方式。那里不用担心。不过,您问,这是否是“最佳”方式。但是,人们应该承认,当与单例结合使用时,
instancetype
和[[self alloc] init]
可能会产生误导。的好处
instancetype
是,它是一种明确的方式,可以声明类可以被子类化,而无需id
像过去那样求助于 类型。但是
static
在这种方法中存在子类化的挑战。如果ImageCache
和BlobCache
单例都是来自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
然后你可以继续为这个属性编写一个 getter(实现将使用
dispatch_once
你建议的模式):+ (Foo *)sharedFoo { ... }
这样做的好处是,如果 Swift 用户去使用它,他们会做如下事情:
let foo = Foo.shared
请注意,没有
()
,因为我们将其实现为属性。从 Swift 3 开始,这就是通常访问单例的方式。因此,将其定义为属性有助于促进这种互操作性。顺便说一句,如果你看看苹果是如何定义他们的单例的,这是他们采用的模式,例如他们的
NSURLSession
单例定义如下:@property (class, readonly, strong) NSURLSession *sharedSession;
另一个非常小的 Swift 互操作性考虑是单例的名称。最好能包含类型的名称,而不是
sharedInstance
. 例如,如果类是Foo
,则可以将单例属性定义为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
在预选赛init
和new
谨慎。
回答by Nayab Muhammad
@interface className : NSObject{
+(className*)SingleTonShare;
}
@implementation className
+(className*)SingleTonShare{
static className* sharedObj = nil;
static dispatch_once_t once = 0;
dispatch_once(&once, ^{
if (sharedObj == nil){
sharedObj = [[className alloc] init];
}
});
return sharedObj;
}
回答by Hancock_Xu
To create thread safe singleton you can do like this:
要创建线程安全的单例,您可以这样做:
@interface SomeManager : NSObject
+ (id)sharedManager;
@end
/* thread safe */
@implementation SomeManager
static id sharedManager = nil;
+ (void)initialize {
if (self == [SomeManager class]) {
sharedManager = [[self alloc] init];
}
}
+ (id)sharedManager {
return sharedManager;
}
@end
and this blog explain singleton very well singletons in objc/cocoa
这个博客很好地解释了 objc/cocoa 中的单例
回答by Rohit Kashyap
//Create Singleton
+( instancetype )defaultDBManager
{
static dispatch_once_t onceToken = 0;
__strong static id _sharedObject = nil;
dispatch_once(&onceToken, ^{
_sharedObject = [[self alloc] init];
});
return _sharedObject;
}
//In it method
-(instancetype)init
{
self = [super init];
if(self)
{
//Do your custom initialization
}
return self;
}