2009-05-06 9 views
2

Ich benutze das Singleton-Muster an mehreren Stellen in einer Anwendung, und ich bekomme Speicherverlust Fehler von clang bei der Analyse des Codes.Objective-C Singletons und LLVM/clang leak warnings

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) 

Ich verwende diese Einstellungen für scan-build:

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

Ich bin ziemlich sicher, dass der Code in der Singleton ganz gut ist - immerhin ist es der gleiche Code hier verwiesen Stack Overflow sowie in der Dokumentation von Apple - aber ich würde gerne die Speicherleck-Warnung aussortieren, damit mein Scan-Build erfolgreich ist.

Antwort

6

kann ich außerordentlich dicht sein zu sein, aber sicherlich Ihre Linie 5

[[self alloc] init]; 

ordnet ein Objekt der Klasse Typ enthält, und wirft ihn sofort weg? Möchten Sie nicht

_sharedMyClass = [[self alloc] init]; 

?

+0

Eigentlich die '-init' Methode setzt die statische Variable _sharedMyClass, also ist das in Ordnung. Siehe: http://stackoverflow.com/questions/145154/what-does-your-objective-c-singleton-look-like/145221#145221 – pix0r

+2

Dann würde ich vermuten, dass CLANG nicht schlau genug ist zu folgern, dass die [ Self-Alloc] return setzt den Self-Parameter, der von der init-Methode gespeichert wird. Es denkt also, es ist einfach verloren (wie ich es getan habe, ohne mehr Kontext). –

+0

LOL, du hast Recht. Vielen Dank. – pix0r

0

Sie auch wahrscheinlich diese auch drin haben ...

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

Der Grund, warum Sie nicht in init wurden Speicherung ist, weil Sie es in dem Verfahren wurden die Speicherung, die aufgerufen Alloc. Dies ist das Muster, das Apple in ihren Beispielen hat. Wenn Sie den Wert auch in Ihrer Init speichern, ist alles in Ordnung und die Warnung verschwindet. Ich würde die allocWithZone-Implementierung alleine lassen.

4

Sie können in einem einfachen, ein Verfahren, GCD-basierten Singleton Implementierung interessiert sein (und damit 10.6+ nur) geschrieben am Mike Ash's site:

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

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

Danke, sieht gut aus .. Leider bin ich eigentlich auf der iPhone-Plattform, also ist das noch keine Option. – pix0r

+2

GCD ist in iOS 4 verfügbar, also muss es sein. dispatch_once ist definitiv der Weg für Singletons. –

5

Apple hat seit aktualisiert recommended singleton code den statischen Analysator passieren :

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

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

Jetzt +sharedManager ruft Super die -allocWithZone: und ordnet die Rückkehr von -init und -allocWithZone: ist die Singleton gibt nur eine beibehaltene sharedInsta nce.

Edit:

Warum behalten die in + allocWithZone :?

+ allocWithZone: wird überschrieben, da jemand, der MyGizmoClass verwendet, den Singleton umgehen kann, indem er [[MyGizmoClass alloc] init] anstelle von [MyGizmoClass sharedManager] aufruft. Es wird beibehalten, da erwartet wird, dass + alloc immer ein Objekt mit einer Retain-Anzahl von +1 zurückgibt.

Jeder Aufruf von + alloc sollte mit einer -Abgabe oder -Autorelease abgeglichen werden, sodass ohne die Beibehaltung in + allocWithZone: die gemeinsam genutzte Instanz unter anderen Benutzern freigegeben werden kann.

+0

Ich mag diesen Ansatz, aber ich verstehe nicht die Notwendigkeit für die beibehalten in allocWithZone. Kann mir das jemand erklären? – n8gray

+0

Beantwortet mit einer Bearbeitung meiner ursprünglichen Antwort. –

1

Sie referenzieren self in einer Klassenmethode! Großes Nein! Zweitens rufen Sie [[self alloc] init] an und werfen die Instanz einfach weg. Sie sollten die Singleton-Referenz in der Klassenmethode zuweisen, und nicht in init, wie ich vermute, dass Sie tun.Als nächstes gibt es keine wirkliche Garantie, dass _sharedMyClass auf Null initialisiert wird. Sie sollten es explizit auf nil initialisieren.

static MyClass *_sharedMyClass = nil; 

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