Como deve ser meu singleton do Objective-C?

Meu método de accessr singleton geralmente é uma variante de:

static MyClass *gInstance = NULL; + (MyClass *)instance { @synchronized(self) { if (gInstance == NULL) gInstance = [[self alloc] init]; } return(gInstance); } 

O que eu poderia estar fazendo para melhorar isso?

   

Outra opção é usar o método de +(void)initialize . Da documentação:

O runtime envia initialize para cada class em um programa exatamente uma vez antes de a class, ou qualquer class que herda dele, receber sua primeira mensagem de dentro do programa. (Assim, o método nunca pode ser chamado se a class não for usada.) O tempo de execução envia a mensagem de initialize para classs de maneira segura para thread. As superclasss recebem essa mensagem antes de suas subclasss.

Então você poderia fazer algo parecido com isto:

 static MySingleton *sharedSingleton; + (void)initialize { static BOOL initialized = NO; if(!initialized) { initialized = YES; sharedSingleton = [[MySingleton alloc] init]; } } 
 @interface MySingleton : NSObject { } + (MySingleton *)sharedSingleton; @end @implementation MySingleton + (MySingleton *)sharedSingleton { static MySingleton *sharedSingleton; @synchronized(self) { if (!sharedSingleton) sharedSingleton = [[MySingleton alloc] init]; return sharedSingleton; } } @end 

[Fonte]

Por minha outra resposta abaixo, acho que você deveria estar fazendo:

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

Como Kendall postou um singleton thread-safe que tenta evitar o bloqueio de custos, eu pensei que iria lançá-lo também:

 #import  static void * volatile sharedInstance = nil; + (className *) sharedInstance { while (!sharedInstance) { className *temp = [[self alloc] init]; if(!OSAtomicCompareAndSwapPtrBarrier(0x0, temp, &sharedInstance)) { [temp release]; } } return sharedInstance; } 

Ok, deixe-me explicar como isso funciona:

  1. Fast case: Na execução normal, sharedInstance já foi definido, portanto o loop while nunca é executado e a function retorna depois de simplesmente testar a existência da variável;

  2. Caso lento: Se sharedInstance não existe, então uma instância é alocada e copiada para ele usando um Compare And Swap (‘CAS’);

  3. Caso sharedInstance : Se dois encadeamentos tentarem chamar sharedInstance ao mesmo tempo AND sharedInstance não existir ao mesmo tempo, ambos inicializarão novas instâncias do singleton e tentarão colocá-lo em posição. Qualquer um que ganhe os retornos do CAS imediatamente, aquele que perder libera a instância que acabou de alocar e retorna o sharedInstance (agora definido). O único OSAtomicCompareAndSwapPtrBarrier atua como uma barreira de gravação para o encadeamento de configuração e uma barreira de leitura do encadeamento de teste.

 estático MyClass * sharedInst = nil;

 + (id) sharedInstance
 {
     @synchronize (self) {
         if (sharedInst == nil) {
             / * sharedInst configurado no init * /
             [[auto alloc] init];
         }
     }
     return sharedInst;
 }

 - (id) init
 {
     if (sharedInst! = nil) {
         [NSException raise: NSInternalInconsistencyException
             format: @ "[% @% @] não pode ser chamado. use + [% @% @] em vez de"],
             NSStringFromClass ([auto class]), NSStringFromSelector (_cmd), 
             NSStringFromClass ([auto class]),
             NSStringFromSelector (@selector (sharedInstance) "];
     } else if (self = [super init]) {
         sharedInst = self;
         / * Qualquer que seja a class específica aqui * /
     }
     return sharedInst;
 }

 / * Estes provavelmente não fazem nada em
    um aplicativo do GC.  Mantém o solteirão
    como um singleton real em um
    aplicativo não CG
 * /
 - (NSUInteger) retainCount
 {
     return NSUIntegerMax;
 }

 - (oneway void) lançamento
 {
 }

 - (id) reter
 {
     return sharedInst;
 }

 - (id) autorelease
 {
     return sharedInst;
 }

Edit: Esta implementação obsoleta com o ARC. Por favor, dê uma olhada em Como eu implemento um singleton Objective-C que é compatível com o ARC? para implementação correta.

Todas as implementações de boot que li em outras respostas compartilham um erro comum.

 + (void) initialize { _instance = [[MySingletonClass alloc] init] // < ----- Wrong! } + (void) initialize { if (self == [MySingletonClass class]){ // <----- Correct! _instance = [[MySingletonClass alloc] init] } } 

A documentação da Apple recomenda que você verifique o tipo de class em seu bloco de boot. Porque as subclasss chamam a boot por padrão. Existe um caso não óbvio onde subclasss podem ser criadas indiretamente através do KVO. Por se você adicionar a seguinte linha em outra class:

 [[MySingletonClass getInstance] addObserver:self forKeyPath:@"foo" options:0 context:nil] 

O Objective-C criará implicitamente uma subclass de MySingletonClass, resultando em um segundo disparo de +initialize .

Você pode achar que deve verificar implicitamente a boot duplicada em seu bloco de boot como tal:

 - (id) init { < ----- Wrong! if (_instance != nil) { // Some hack } else { // Do stuff } return self; } 

Mas você vai se atirar no pé; ou pior, dar a outro desenvolvedor a oportunidade de atirar no próprio pé.

 - (id) init { < ----- Correct! NSAssert(_instance == nil, @"Duplication initialization of singleton"); self = [super init]; if (self){ // Do stuff } return self; } 

TL; DR, aqui está minha implementação

 @implementation MySingletonClass static MySingletonClass * _instance; + (void) initialize { if (self == [MySingletonClass class]){ _instance = [[MySingletonClass alloc] init]; } } - (id) init { ZAssert (_instance == nil, @"Duplication initialization of singleton"); self = [super init]; if (self) { // Initialization } return self; } + (id) getInstance { return _instance; } @end 

(Substitua o ZAssert pela nossa própria macro de asserção ou apenas pelo NSAssert.)

Uma explicação completa do código macro Singleton está no blog Cocoa With Love

http://cocoawithlove.com/2008/11/singletons-appdelegates-and-top-level.html .

Eu tenho uma variação interessante em sharedInstance que é thread-safe, mas não bloqueia após a boot. Ainda não tenho certeza o suficiente para modificar a resposta principal, conforme solicitado, mas apresento para uma discussão mais aprofundada:

 // Volatile to make sure we are not foiled by CPU caches static volatile ALBackendRequestManager *sharedInstance; // There's no need to call this directly, as method swizzling in sharedInstance // means this will get called after the singleton is initialized. + (MySingleton *)simpleSharedInstance { return (MySingleton *)sharedInstance; } + (MySingleton*)sharedInstance { @synchronized(self) { if (sharedInstance == nil) { sharedInstance = [[MySingleton alloc] init]; // Replace expensive thread-safe method // with the simpler one that just returns the allocated instance. SEL origSel = @selector(sharedInstance); SEL newSel = @selector(simpleSharedInstance); Method origMethod = class_getClassMethod(self, origSel); Method newMethod = class_getClassMethod(self, newSel); method_exchangeImplementations(origMethod, newMethod); } } return (MySingleton *)sharedInstance; } 

Resposta curta: Fabuloso.

Resposta longa: algo como ….

 static SomeSingleton *instance = NULL; @implementation SomeSingleton + (id) instance { static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ if (instance == NULL){ instance = [[super allocWithZone:NULL] init]; } }); return instance; } + (id) allocWithZone:(NSZone *)paramZone { return [[self instance] retain]; } - (id) copyWithZone:(NSZone *)paramZone { return self; } - (id) autorelease { return self; } - (NSUInteger) retainCount { return NSUIntegerMax; } - (id) retain { return self; } @end 

Não deixe de ler o header dispatch / once.h para entender o que está acontecendo. Nesse caso, os comentários de header são mais aplicáveis ​​que os documentos ou a página do manual.

Eu rolei singleton em uma class, para que outras classs possam herdar propriedades singleton.

Singleton.h:

 static id sharedInstance = nil; #define DEFINE_SHARED_INSTANCE + (id) sharedInstance { return [self sharedInstance:&sharedInstance]; } \ + (id) allocWithZone:(NSZone *)zone { return [self allocWithZone:zone forInstance:&sharedInstance]; } @interface Singleton : NSObject { } + (id) sharedInstance; + (id) sharedInstance:(id*)inst; + (id) allocWithZone:(NSZone *)zone forInstance:(id*)inst; @end 

Singleton.m:

 #import "Singleton.h" @implementation Singleton + (id) sharedInstance { return [self sharedInstance:&sharedInstance]; } + (id) sharedInstance:(id*)inst { @synchronized(self) { if (*inst == nil) *inst = [[self alloc] init]; } return *inst; } + (id) allocWithZone:(NSZone *)zone forInstance:(id*)inst { @synchronized(self) { if (*inst == nil) { *inst = [super allocWithZone:zone]; return *inst; // assignment and return on first allocation } } return nil; // on subsequent allocation attempts return nil } - (id)copyWithZone:(NSZone *)zone { return self; } - (id)retain { return self; } - (unsigned)retainCount { return UINT_MAX; // denotes an object that cannot be released } - (void)release { //do nothing } - (id)autorelease { return self; } @end 

E aqui está um exemplo de alguma class, que você quer se tornar um solteirão.

 #import "Singleton.h" @interface SomeClass : Singleton { } @end @implementation SomeClass DEFINE_SHARED_INSTANCE; @end 

A única limitação sobre a class Singleton é que ela é uma subclass NSObject. Mas na maioria das vezes eu uso singletons no meu código, eles são na verdade subclasss NSObject, então essa class realmente facilita minha vida e torna o código mais limpo.

Isso funciona em um ambiente não coletado também.

 @interface MySingleton : NSObject { } +(MySingleton *)sharedManager; @end @implementation MySingleton static MySingleton *sharedMySingleton = nil; +(MySingleton*)sharedManager { @synchronized(self) { if (sharedMySingleton == nil) { [[self alloc] init]; // assignment not done here } } return sharedMySingleton; } +(id)allocWithZone:(NSZone *)zone { @synchronized(self) { if (sharedMySingleton == nil) { sharedMySingleton = [super allocWithZone:zone]; return sharedMySingleton; // assignment and return on first allocation } } return nil; //on subsequent allocation attempts return nil } -(void)dealloc { [super dealloc]; } -(id)copyWithZone:(NSZone *)zone { return self; } -(id)retain { return self; } -(unsigned)retainCount { return UINT_MAX; //denotes an object that cannot be release } -(void)release { //do nothing } -(id)autorelease { return self; } -(id)init { self = [super init]; sharedMySingleton = self; //initialize here return self; } @end 

Não deve ser seguro e evitar o bloqueio caro após a primeira chamada?

 + (MySingleton*)sharedInstance { if (sharedInstance == nil) { @synchronized(self) { if (sharedInstance == nil) { sharedInstance = [[MySingleton alloc] init]; } } } return (MySingleton *)sharedInstance; } 

Aqui está uma macro que eu juntei:

http://github.com/cjhanson/Objective-C-Optimized-Singleton

É baseado no trabalho aqui por Matt Gallagher Mas mudando a implementação para usar o método swizzling como descrito aqui por Dave MacLachlan do Google .

Saúdo comentários / contribuições.

E se

 static MyClass *gInstance = NULL; + (MyClass *)instance { if (gInstance == NULL) { @synchronized(self) { if (gInstance == NULL) gInstance = [[self alloc] init]; } } return(gInstance); } 

Então você evita o custo de synchronization após a boot?

Para uma discussão aprofundada do padrão singleton em Objective-C, veja aqui:

Usando o padrão singleton em Objective-C

KLSingleton é:

  1. Subclassível (até o n-ésimo grau)
  2. Compatível com ARC
  3. Seguro com alloc e init
  4. Carregado preguiçosamente
  5. Discussão segura
  6. Sem bloqueio (usa + inicializa, não @sincroniza)
  7. Sem macro
  8. Swizzle-free
  9. Simples

KLSingleton

Você não quer sincronizar sozinho … Já que o object self ainda não existe! Você acaba bloqueando um valor de ID temporário. Você quer garantir que ninguém mais possa executar methods de class (sharedInstance, alloc, allocWithZone :, etc), portanto, é necessário sincronizar no object de class:

 @implementation MYSingleton static MYSingleton * sharedInstance = nil; +( id )sharedInstance { @synchronized( [ MYSingleton class ] ) { if( sharedInstance == nil ) sharedInstance = [ [ MYSingleton alloc ] init ]; } return sharedInstance; } +( id )allocWithZone:( NSZone * )zone { @synchronized( [ MYSingleton class ] ) { if( sharedInstance == nil ) sharedInstance = [ super allocWithZone:zone ]; } return sharedInstance; } -( id )init { @synchronized( [ MYSingleton class ] ) { self = [ super init ]; if( self != nil ) { // Insert initialization code here } return self; } } @end 

Só queria deixar isso aqui para não perder. A vantagem disso é que é utilizável no InterfaceBuilder, o que é uma grande vantagem. Isto é retirado de outra pergunta que fiz :

 static Server *instance; + (Server *)instance { return instance; } + (id)hiddenAlloc { return [super alloc]; } + (id)alloc { return [[self instance] retain]; } + (void)initialize { static BOOL initialized = NO; if(!initialized) { initialized = YES; instance = [[Server hiddenAlloc] init]; } } - (id) init { if (instance) return self; self = [super init]; if (self != nil) { // whatever } return self; } 
 static mySingleton *obj=nil; @implementation mySingleton -(id) init { if(obj != nil){ [self release]; return obj; } else if(self = [super init]) { obj = self; } return obj; } +(mySingleton*) getSharedInstance { @synchronized(self){ if(obj == nil) { obj = [[mySingleton alloc] init]; } } return obj; } - (id)retain { return self; } - (id)copy { return self; } - (unsigned)retainCount { return UINT_MAX; // denotes an object that cannot be released } - (void)release { if(obj != self){ [super release]; } //do nothing } - (id)autorelease { return self; } -(void) dealloc { [super dealloc]; } @end 

Eu sei que há muitos comentários sobre essa “pergunta”, mas não vejo muitas pessoas sugerindo usar uma macro para definir o singleton. É um padrão tão comum e uma macro simplifica muito o singleton.

Aqui estão as macros que escrevi com base em várias implementações do Objc que vi.

Singeton.h

 /** @abstract Helps define the interface of a singleton. @param TYPE The type of this singleton. @param NAME The name of the singleton accessor. Must match the name used in the implementation. @discussion Typcially the NAME is something like 'sharedThing' where 'Thing' is the prefix-removed type name of the class. */ #define SingletonInterface(TYPE, NAME) \ + (TYPE *)NAME; /** @abstract Helps define the implementation of a singleton. @param TYPE The type of this singleton. @param NAME The name of the singleton accessor. Must match the name used in the interface. @discussion Typcially the NAME is something like 'sharedThing' where 'Thing' is the prefix-removed type name of the class. */ #define SingletonImplementation(TYPE, NAME) \ static TYPE *__ ## NAME; \ \ \ + (void)initialize \ { \ static BOOL initialized = NO; \ if(!initialized) \ { \ initialized = YES; \ __ ## NAME = [[TYPE alloc] init]; \ } \ } \ \ \ + (TYPE *)NAME \ { \ return __ ## NAME; \ } 

Exemplo de uso:

MyManager.h

 @interface MyManager SingletonInterface(MyManager, sharedManager); // ... @end 

MyManager.m

 @implementation MyManager - (id)init { self = [super init]; if (self) { // Initialization code here. } return self; } SingletonImplementation(MyManager, sharedManager); // ... @end 

Por que uma macro de interface quando está quase vazia? Código de consistência entre os arquivos de header e código; manutenção, caso deseje adicionar mais methods automáticos ou alterá-lo.

Eu estou usando o método initialize para criar o singleton como é usado na resposta mais popular aqui (no momento da escrita).

Com os methods da class Objective C, podemos evitar usar o padrão singleton da maneira usual, a partir de:

 [[Librarian sharedInstance] openLibrary] 

para:

 [Librarian openLibrary] 

Envolvendo a class dentro de outra class que só tem methods de class , dessa forma não há nenhuma chance de criar acidentalmente instâncias duplicadas, já que não estamos criando nenhuma instância!

Eu escrevi um blog mais detalhado aqui 🙂

Para estender o exemplo de @ robbie-hanson …

 static MySingleton* sharedSingleton = nil; + (void)initialize { static BOOL initialized = NO; if (!initialized) { initialized = YES; sharedSingleton = [[self alloc] init]; } } - (id)init { self = [super init]; if (self) { // Member initialization here. } return self; } 

Meu caminho é simples assim:

 static id instanceOfXXX = nil; + (id) sharedXXX { static volatile BOOL initialized = NO; if (!initialized) { @synchronized([XXX class]) { if (!initialized) { instanceOfXXX = [[XXX alloc] init]; initialized = YES; } } } return instanceOfXXX; } 

Se o singleton já estiver inicializado, o bloco LOCK não será inserido. A segunda verificação se (! Initialized) é ter certeza que ainda não está inicializado quando o thread atual adquire o LOCK.

Eu não li todas as soluções, então perdoe se esse código é redundante.

Esta é a implementação mais segura de thread na minha opinião.

 +(SingletonObject *) sharedManager { static SingletonObject * sharedResourcesObj = nil; @synchronized(self) { if (!sharedResourcesObj) { sharedResourcesObj = [[SingletonObject alloc] init]; } } return sharedResourcesObj; } 

Eu costumo usar um código semelhante ao da resposta de Ben Hoffstein (que também saí da Wikipedia). Eu uso isso pelas razões declaradas por Chris Hanson em seu comentário.

No entanto, às vezes eu tenho a necessidade de colocar um singleton em um NIB, e nesse caso eu uso o seguinte:

 @implementation Singleton static Singleton *singleton = nil; - (id)init { static BOOL initialized = NO; if (!initialized) { self = [super init]; singleton = self; initialized = YES; } return self; } + (id)allocWithZone:(NSZone*)zone { @synchronized (self) { if (!singleton) singleton = [super allocWithZone:zone]; } return singleton; } + (Singleton*)sharedSingleton { if (!singleton) [[Singleton alloc] init]; return singleton; } @end 

Deixo a implementação de -retain (etc) para o leitor, embora o código acima seja tudo o que você precisa em um ambiente de garbage collection.

The accepted answer, although it compiles, is incorrect.

 + (MySingleton*)sharedInstance { @synchronized(self) < -------- self does not exist at class scope { if (sharedInstance == nil) sharedInstance = [[MySingleton alloc] init]; } return sharedInstance; } 

Per Apple documentation:

... You can take a similar approach to synchronize the class methods of the associated class, using the Class object instead of self.

Even if using self works, it shouldn't and this looks like a copy and paste mistake to me. The correct implementation for a class factory method would be:

 + (MySingleton*)getInstance { @synchronized([MySingleton class]) { if (sharedInstance == nil) sharedInstance = [[MySingleton alloc] init]; } return sharedInstance; }