Question

J'utilise le motif singleton à plusieurs endroits d'une application et j'obtiens des erreurs de fuite de mémoire de clang lors de l'analyse du code.

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)

J'utilise ces paramètres pour scan-build :

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

Je suis à peu près sûr que le code dans le singleton convient parfaitement. Après tout, il s'agit du même code référencé ici dans Stack Overflow ainsi que dans la documentation d'Apple, mais j'aimerais que l'avertissement concernant les fuites de mémoire soit résolu. mon scan-build renvoie le succès.

Était-ce utile?

La solution

Je suis peut-être exceptionnellement dense, mais votre ligne 5 est sûrement

[[self alloc] init];

alloue un objet du type classe contenant et le jette rapidement? Ne veux-tu pas

_sharedMyClass = [[self alloc] init];

?

Autres conseils

Apple a depuis mis à jour son code de singleton recommandé pour passer l'analyseur statique:

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

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

Now + sharedManager appelle le -allocWithZone: du super et attribue le retour de -init , et le -allocWithZone du singleton: renvoie simplement une instance partagée non conservée.

Modifier:

Pourquoi conserver dans + allocWithZone:?

+ allocWithZone: est remplacé car une personne utilisant MyGizmoClass pourrait contourner le singleton en appelant [[MyGizmoClass alloc] init] au lieu de [MyGizmoClass sharedManager]. Il est conservé, car + alloc devrait toujours renvoyer un objet avec un nombre de retenues égal à +1.

Chaque appel à + alloc doit être équilibré avec -release ou -autorelease. Par conséquent, sans la retenue dans + allocWithZone :, l'instance partagée pourrait éventuellement être désallouée sous d'autres utilisateurs.

Vous pouvez être intéressé par une implémentation de singleton simple, à une méthode, basée sur GCD (et donc 10.6+ uniquement) postée le Le site de Mike Ash :

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

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

Vous faites référence à self dans une méthode de classe! Big non-non! Deuxièmement, vous appelez [[self alloc] init] et vous vous contentez de jeter l'instance. Vous devez assigner la référence singleton dans la méthode class et non dans init comme je le suppose. Ensuite, rien ne garantit que _sharedMyClass sera initialisé à zéro. Vous devez explicitement l’initialiser sur nil .

static MyClass *_sharedMyClass = nil;

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

Vous en avez probablement aussi eu ici aussi

+ (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
}

Si vous ne le stockiez pas dans init, c'est parce que vous le stockiez dans la méthode que alloc a appelée. C'est le modèle qu'Apple utilise dans ses exemples. Si vous enregistrez également la valeur dans votre init, tout va bien et l'avertissement disparaît. Je laisserais seul l'implémentation allocWithZone.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top