ios - Criar um singleton usando o dispatch_once do GCD no Objective C




objective-c grand-central-dispatch (6)

instancetype

instancetype é apenas uma das muitas extensões de linguagem para Objective-C , com mais sendo adicionadas a cada nova versão.

Saiba disso, ame-o.

E tome-o como um exemplo de como prestar atenção aos detalhes de baixo nível pode dar-lhe insights sobre maneiras novas e poderosas de transformar o Objective-C.

Consulte aqui: 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;
}

Se você pode segmentar o iOS 4.0 ou superior

Usando o GCD, é a melhor maneira de criar um singleton no Objective C (thread safe)?

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

Dave está correto, isso está perfeitamente bem. Você pode verificar os documentos da Apple sobre como criar um singleton para obter dicas sobre a implementação de alguns dos outros métodos para garantir que apenas um possa ser criado se as classes optarem por NÃO usar o método sharedFoo.


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

Para criar um singleton seguro para threads, você pode fazer assim:

@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

e este blog explicam singleton muito bem singletons em objc / cacau


Você pergunta se esta é a "melhor maneira de criar singleton".

Alguns pensamentos:

  1. Primeiro, sim, esta é uma solução segura para threads. Esse padrão dispatch_once é a maneira moderna e segura de gerar singletons em Objective-C. Não se preocupe lá.

  2. Você perguntou, no entanto, se essa é a melhor maneira de fazer isso. Deve-se reconhecer, no entanto, que o instancetype e [[self alloc] init] é potencialmente enganador quando usado em conjunto com singletons.

    O benefício do instancetype é que é uma maneira inequívoca de declarar que a classe pode ser subclassificada sem recorrer a um tipo de id , como tivemos que fazer no passado.

    Mas a static nesse método apresenta desafios de subclasses. E se os singletons BlobCache e BlobCache fossem subclasses de uma superclasse Cache sem implementar seu próprio método sharedCache ?

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

    Para que isso funcione, você precisa ter certeza de que as subclasses implementam seu próprio sharedInstance (ou o que você chama para sua classe particular).

    Bottom line, seu sharedInstance original parece que vai suportar subclasses, mas não vai. Se você pretende suportar subclasses, inclua, no mínimo, documentação que avise os futuros desenvolvedores de que eles devem substituir esse método.

  3. Para melhor interoperabilidade com o Swift, você provavelmente vai querer definir isso como uma propriedade, não um método de classe, por exemplo:

    @interface Foo : NSObject
    @property (class, readonly, strong) Foo *sharedFoo;
    @end

    Então você pode ir em frente e escrever um getter para essa propriedade (a implementação usaria o padrão dispatch_once você sugeriu):

    + (Foo *)sharedFoo { ... }

    O benefício disso é que, se um usuário do Swift for usá-lo, ele fará algo como:

    let foo = Foo.shared

    Note que não há () , porque nós o implementamos como uma propriedade. Iniciando o Swift 3, é assim que os singletons são geralmente acessados. Então defini-lo como uma propriedade ajuda a facilitar essa interoperabilidade.

    Como um aparte, se você observar como a Apple está definindo seus singletons, esse é o padrão que eles adotaram, por exemplo, seu singleton NSURLSession é definido da seguinte forma:

    @property (class, readonly, strong) NSURLSession *sharedSession;
  4. Outra consideração de interoperabilidade Swift muito menor era o nome do singleton. É melhor se você puder incorporar o nome do tipo, em vez de sharedInstance . Por exemplo, se a classe for Foo , você poderá definir a propriedade singleton como sharedFoo . Ou, se a classe for DatabaseManager , você poderá chamar a propriedade sharedManager . Então os usuários do Swift poderiam fazer:

    let foo = Foo.shared
    let manager = DatabaseManager.shared

    Claramente, se você realmente quer usar sharedInstance , você sempre pode declarar o nome Swift se você quiser:

    @property (class, readonly, strong) Foo* sharedInstance NS_SWIFT_NAME(shared);

    Claramente, ao escrever o código Objective-C, não devemos deixar que a interoperabilidade do Swift supere outras considerações de design, mas ainda assim, se pudermos escrever código que suporte ambas as linguagens, é preferível.

  5. Eu concordo com outras pessoas que apontam que, se você quer que isso seja um verdadeiro singleton, onde os desenvolvedores não podem / não devem (acidentalmente) instanciar suas próprias instâncias, o qualificador unavailable no init e no new é prudente.


Você pode evitar que a classe seja alocada sobrescrevendo o método de alocação.

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




grand-central-dispatch