在Objective C中使用GCD的dispatch_once创建单例

时间:2011-04-19 17:07:11

标签: ios objective-c singleton grand-central-dispatch

如果您可以定位iOS 4.0或更高版本

使用GCD,它是在Objective C(线程安全)中创建单例的最佳方法吗?

+ (instancetype)sharedInstance
{
    static dispatch_once_t once;
    static id sharedInstance;
    dispatch_once(&once, ^{
        sharedInstance = [[self alloc] init];
    });
    return sharedInstance;
}

10 个答案:

答案 0 :(得分:212)

这是创建类实例的完全可接受且线程安全的方法。从技术上讲,它可能不是“单例”(因为它们只能有1个这样的对象),但只要你只使用[Foo sharedFoo]方法来访问对象,这就足够了。

答案 1 :(得分:35)

instancetype

instancetype只是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;
}

答案 2 :(得分:32)

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

@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

答案 3 :(得分:6)

您可以避免使用覆盖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;
}

答案 4 :(得分:5)

戴夫是对的,这很好。您可能需要查看Apple's docs on creating a singleton以获取有关实现其他一些方法的提示,以确保在类选择不使用sharedFoo方法时只能创建一个方法。

答案 5 :(得分:4)

如果你想确保[[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;
}

这允许[[MyClass alloc] init]和[MyClass sharedInstance]的任何组合返回相同的对象; [MyClass sharedInstance]会更有效率。工作原理:[MyClass sharedInstance]将调用[[MyClass alloc] init]一次。其他代码也可以多次调用它。 init的第一个调用者将执行“正常”初始化并将单例对象存储在init方法中。以后对init的任何调用都将完全忽略返回的alloc并返回相同的sharedInstance; alloc的结果将被解除分配。

+ sharedInstance方法将一如既往地工作。如果它不是第一个调用[[MyClass alloc] init]的调用者,那么init的结果不是alloc调用的结果,但是没关系。

答案 6 :(得分:1)

你问这是否是“创建单身人士的最佳方式”。

一些想法:

  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开始,这就是一般访问单身人士的方式。因此,将其定义为属性有助于促进互操作性。

    顺便说一句,如果你看一下Apple如何定义他们的单身人士,这就是他们采用的模式,例如:他们的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限定符init 1}}和new是谨慎的。

答案 7 :(得分:0)

要创建线程安全单例,您可以这样做:

@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

此博客很好地解释了单身singletons in objc/cocoa

答案 8 :(得分:0)

//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;
}

答案 9 :(得分:0)

@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;
}