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

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-30 19:38:02  来源:igfitidea点击:

Create singleton using GCD's dispatch_once in Objective-C

iosobjective-csingletongrand-central-dispatch

提问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

实例类型

instancetypeis 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 的强大新方法。

Refer here: instancetype

请参阅此处:实例类型



+ (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:

一些想法:

  1. First, yes, this is a thread-safe solution. This dispatch_oncepattern is the modern, thread-safe way to generate singletons in Objective-C. No worries there.

  2. You asked, though, whether this is the "best" way to do it. One should acknowledge, though, that the instancetypeand [[self alloc] init]is potentially misleading when used in conjunction with singletons.

    The benefit of instancetypeis that it's an unambiguous way of declaring that the class can be subclassed without resorting to a type of id, like we had to do in yesteryear.

    But the staticin this method presents subclassing challenges. What if ImageCacheand BlobCachesingletons were both subclasses from a Cachesuperclass without implementing their own sharedCachemethod?

    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 sharedInstancelookslike 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.

  3. 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_oncepattern 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 NSURLSessionsingleton is defined as follows:

    @property (class, readonly, strong) NSURLSession *sharedSession;
    
  4. 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 was Foo, you might define the singleton property as sharedFoo. Or if the class was DatabaseManager, you might call the property sharedManager. 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.

  5. 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 unavailablequalifier on initand newis prudent.

  1. 首先,是的,这是一个线程安全的解决方案。这种dispatch_once模式是在 Objective-C 中生成单例的现代、线程安全的方式。那里不用担心。

  2. 不过,您问,这是否是“最佳”方式。但是,人们应该承认,当与单例结合使用时,instancetype[[self alloc] init]可能会产生误导。

    的好处instancetype是,它是一种明确的方式,可以声明类可以被子类化,而无需id像过去那样求助于 类型。

    但是static在这种方法中存在子类化的挑战。如果ImageCacheBlobCache单例都是来自Cache超类的子类,而没有实现自己的sharedCache方法呢?

    ImageCache *imageCache = [ImageCache sharedCache];  // fine
    BlobCache *blobCache = [BlobCache sharedCache];     // error; this will return the aforementioned ImageCache!!!
    

    为此,您必须确保子类实现它们自己的sharedInstance(或您为特定类调用的任何方法)方法。

    最重要的是,您的原始sharedInstance看起来会支持子类,但不会。如果您打算支持子类化,至少包括警告未来开发人员他们必须覆盖此方法的文档。

  3. 为了与 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;
    
  4. 另一个非常小的 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 互操作性超过其他设计考虑,但是,如果我们可以编写优雅地支持这两种语言的代码,那就更好了。

  5. 我同意其他人谁指出,如果你想这是一个真正的单身人士,开发人员不能/不应该(不小心)实例自己的情况下,unavailable在预选赛initnew谨慎。

回答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;
}