Domanda

Sto usando il modello singleton in diversi punti di un'applicazione e ricevo errori di perdita di memoria da clang durante l'analisi del codice.

static MyClass *_sharedMyClass;
+ (MyClass *)sharedMyClass {
  @synchronized(self) {
    if (_sharedMyClass == nil)
      [[self alloc] init];
  }
  return _sharedMyClass;
}

// clang error: Object allocated on line 5 is no longer referenced after this point and has a retain count of +1 (object leaked)

Sto usando queste impostazioni per scan-build :

scan-build -v -v -v -V -k xcodebuild

Sono abbastanza certo che il codice nel singleton va bene - dopotutto, è lo stesso codice a cui fa riferimento qui su Stack Overflow e nella documentazione di Apple - ma vorrei che l'avviso di perdita di memoria venisse risolto così il mio scan-build restituisce successo.

È stato utile?

Soluzione

Potrei essere eccezionalmente denso, ma sicuramente la tua linea 5

[[self alloc] init];

alloca un oggetto del tipo di classe contenente e lo butta via immediatamente? Non vuoi

_sharedMyClass = [[self alloc] init];

Altri suggerimenti

Da allora Apple ha aggiornato codice singleton consigliato per passare l'analizzatore statico:

+ (MyGizmoClass*)sharedManager
{
    if (sharedGizmoManager == nil) {
        sharedGizmoManager = [[super allocWithZone:NULL] init];
    }
    return sharedGizmoManager;
}

+ (id)allocWithZone:(NSZone *)zone
{
    return [[self sharedManager] retain];
}

Ora + sharedManager chiama il -allocWithZone di super: e assegna il ritorno di -init e il -allocWithZone del singleton: restituisce solo un'istanza condivisa mantenuta.

Modifica:

Perché conservare in + allocWithZone :?

+ allocWithZone: viene sostituito perché qualcuno che utilizza MyGizmoClass potrebbe aggirare il singleton chiamando [[MyGizmoClass alloc] init] invece di [MyGizmoClass sharedManager]. Viene mantenuto perché + alloc dovrebbe restituire sempre un oggetto con un conteggio di mantenimento di +1.

Ogni chiamata a + alloc dovrebbe essere bilanciata con un -release o -autorelease, quindi senza la conservazione in + allocWithZone :, l'istanza condivisa potrebbe potenzialmente essere distribuita da altri utenti.

Potresti essere interessato a una semplice implementazione single-method basata su GCD (e quindi solo 10.6+) pubblicata su Sito di Mike Ash :

+ (id)sharedFoo
{
    static dispatch_once_t pred;
    static Foo *foo = nil;

    dispatch_once(&pred, ^{ foo = [[self alloc] init]; });
    return foo;
}

Stai facendo riferimento a self in un metodo di classe! Grande no-no! In secondo luogo, stai chiamando [[self alloc] init] e stai semplicemente gettando via l'istanza. Dovresti assegnare il riferimento singleton nel metodo class e non in init come immagino tu stia facendo. Successivamente, non vi è alcuna reale garanzia che _sharedMyClass verrà inizializzato su zero. Dovresti inizializzarlo esplicitamente su nil .

static MyClass *_sharedMyClass = nil;

+ (MyClass *)sharedMyClass {
  @synchronized(self) {
    if (_sharedMyClass == nil)
      _sharedMyClass = [[MyClass alloc] init];
  }
  return _sharedMyClass;
}

Probabilmente avevi anche questo dentro ...

+ (id)allocWithZone:(NSZone *)zone {
    @synchronized(self) {
        if (sharedInstance == nil) {
            sharedInstance = [super allocWithZone:zone];
            return sharedInstance;  // assignment and return on first allocation
        }
    }
    return nil; // on subsequent allocation attempts return nil
}

Il motivo per cui non lo stavi memorizzando in init è perché lo stavi memorizzando nel metodo chiamato alloc. Questo è lo schema che Apple ha nei loro esempi. Se salvate il valore anche nel vostro init, tutto va bene e l'avvertimento scompare. Lascerei da sola l'implementazione allocWithZone.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top