2009-05-06 18 views
2

アプリケーションのいくつかの場所でシングルトンパターンを使用しています。コードを解析するときにメモリリークエラーがclangから発生しています。Objective-C SingletonsとLLVM/clangのリーク警告

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) 

私はscan-buildためにこれらの設定を使用しています:

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

私はシングルトンのコードがうまくされていることをかなり確信している - すべての後に、それがここで参照同じコードですAppleのドキュメンテーションと同様にスタックオーバーフローが発生しますが、メモリリーク警告を整理して、スキャンビルドが成功を返すようにしたいと考えています。

答えて

6

私は非常に密であることことができるが、きっとあなたのラインは5

[[self alloc] init]; 

含むクラス型のオブジェクトを割り当て、速やかにそれを捨て?したくないですか

_sharedMyClass = [[self alloc] init]; 

?あなたはまた、おそらくあまりにもそこにこれを持っていた

+0

実際には、 '-init'メソッドはstatic _sharedMyClass変数を設定します。これは問題ありません。参照してください:http://stackoverflow.com/questions/145154/what-does-your-objective-c-singleton-look-like/145221#145221 – pix0r

+2

私は推測するには、 self alloc] returnは実際にはselfパラメータを設定しています。これはinitメソッドによって保存されます。したがって、それはちょうど失われたと思う(私がしたように、より多くの文脈なしに)。 –

+0

LOL、あなたは正しいです。ありがとう。 – pix0r

0

あなたが呼ばれるのallocメソッドに格納されたので...あなたはINITに格納されていない

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

理由があります。これはAppleがその例で示しているパターンです。 initに値を保存しても、すべて正常で、警告は消えてしまいます。私はallocWithZone実装だけを残しておきます。

4

あなたは、単純な、一方法に興味がある可能性があり、シングルトンの実装をGCDベース(ひいては10.6以降のみ)が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

ありがとう、いいね。残念ながら私は実際にはiPhoneプラットフォーム上にいるので、まだオプションではありません。 – pix0r

+2

GCDはiOS 4で利用できます。 dispatch_onceは間違いなくシングルトンに行く方法です。 –

5
Appleは以来 recommended singleton codeは静的アナライザを渡すように更新しました

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

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

は今+sharedManager通話スーパーの-allocWithZone:-initのリターンを割り当て、シングルトンの-allocWithZone:だけ保持sharedInstaを返します。 nce。

編集:

なぜ+ allocWithZoneに保持されますか:

+ allocWithZone:MyGizmoClassを使用している人が、[MyGizmoClass sharedManager]の代わりに[[MyGizmoClass alloc] init]を呼び出してシングルトンを回避できるため、オーバーライドされています。 + allocは常に+1の保持数を持つオブジェクトを返すため、保持されます。

+ allocのすべての呼び出しは、-releaseまたは-autoreleaseでバランスする必要があります。したがって、+ allocWithZone:を保持しないと、共有インスタンスが他のユーザーの下から割り当て解除される可能性があります。

+0

私はこのアプローチが好きですが、私はallocWithZoneでの保持の必要性を理解していません。誰かが私にそれを説明できますか? – n8gray

+0

私の元の回答への編集で回答しました。 –

1

クラスメソッドでselfを参照しています。ビッグ・ノー!第二に、あなたは[[self alloc] init]に電話しているだけで、インスタンスを破棄しています。私はあなたがやっていると推測しているように、あなたはクラスメソッドでシングルトンリファレンスを割り当てて、initには割り当ててはいけません。次に、_sharedMyClassがゼロに初期化されるという実際の保証はありません。明示的にそれをnilに初期化する必要があります。

static MyClass *_sharedMyClass = nil; 

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