Assicurarsi di leggere la discussione su questo domanda/risposta, anche. Why should we separate alloc and init calls to avoid deadlocks in Objective-C?
Espansione sul problema della condizione di gara; la correzione reale non deve avere l'inizializzazione indeterminata all'interno dell'applicazione. indeterminati o pigri risultati di inizializzazione nel comportamento che può facilmente cambiare a causa di cambiamenti apparentemente innocue - configurazione, modifiche al codice "estranei", ecc ...
Meglio per inizializzare in modo esplicito sottosistemi su un noto buon punto in la durata della vita del programma. Cioè rilasciare [MyClass sharedInstance];
nel metodo applicationDidFinishLaunching:
del delegato dell'App se si in realtà è necessario che il sottosistema sia inizializzato all'inizio del programma (o lo si sposta anche prima, se si desidera essere in più difensivo).
Meglio ancora per spostare completamente l'inizializzazione su quel metodo. Cioè [MyClass initializeSharedInstance];
dove +sharedInstance
asserisce() se tale metodo non viene chiamato per primo.
Per quanto io sia un fan della convenienza, 25 anni di programmazione ObjC mi hanno insegnato che l'inizializzazione pigra è fonte di più mal di testa per la manutenzione e il refactoring di quanto valga.
Mentre la condizione di competizione esiste descritto di seguito, questo codice non risolve ciò che è descritto qui di seguito. Lo ha fatto per un paio di decenni quando non ci siamo preoccupati della concorrenza negli inizializzatori di istanze condivise. Lasciando il codice sbagliato per la prosperità.
Ricorda che per le risposte altrimenti corrette di Colin e Harald, c'è una condizione di gara molto sottile che potrebbe portarti ad un mondo di guai.
Vale a dire, se il -init
della classe allocata si verifica per chiamare il metodo sharedInstance
, lo farà prima che la variabile sia impostata. In entrambi i casi porterà a un punto morto.
Questa è l'unica volta in cui si desidera separare l'alloc e l'init. Cribbing codice di Colin, perché è la soluzione migliore (supponendo che Mac OS X):
+(MyClass *)sharedInstance
{
static MyClass *sharedInstance = nil;
static dispatch_once_t pred;
// partial fix for the "new" concurrency issue
if (sharedInstance) return sharedInstance;
// partial because it means that +sharedInstance *may* return an un-initialized instance
// this is from https://stackoverflow.com/questions/20895214/why-should-we-separate-alloc-and-init-calls-to-avoid-deadlocks-in-objective-c/20895427#20895427
dispatch_once(&pred, ^{
sharedInstance = [MyClass alloc];
sharedInstance = [sharedInstance init];
});
return sharedInstance;
}
nota questo funziona solo su Mac OS X; X 10.6+ e iOS 4.0+, in particolare.Nei sistemi operativi precedenti, in cui i blocchi non sono disponibili, utilizzare una serratura o uno dei vari modi per fare qualcosa una volta che non è basato sui blocchi.
Il motivo sopra riportato non impedisce il problema descritto nel testo e causa un deadlock quando viene rilevato. Il problema è che il dispatch_once()
non è rientranti e, quindi, se lo init
chiama sharedInstance
, città di cuneo.
potreste essere interessati a leggere questo (http: //steve.yegge. googlepages.com/singleton-considered-stupid). – sand
Nonostante l'odio di yegge per i singleton, hanno sicuramente uno scopo su iPhone. Ma se stai semplicemente creando uno 'spazio dei nomi', usa invece i metodi di classe. – bentford
@bentford - Ho programmato iPhone da 4 anni, vecchio codice e nuovo. Ho visto singleton usati una volta (in un codice piuttosto scadente). –