質問

アプリケーションの複数の場所でシングルトンパターンを使用しています。コードの分析時に 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のドキュメントで参照されているコードと同じです-しかし、メモリリーク警告を整理したいのでscan-buildは成功を返します。

役に立ちましたか?

解決

私は非常に密集しているかもしれませんが、確かにあなたの行5

[[self alloc] init];

収容クラス型のオブジェクトを割り当て、すぐにそれを捨てますか?したくないですか

_sharedMyClass = [[self alloc] init];

他のヒント

Appleはその後、推奨されるシングルトンコードで静的アナライザーを渡す:

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

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

Now + sharedManager はスーパーの -allocWithZone:を呼び出し、 -init の戻り値とシングルトンの -allocWithZone:は保持されたsharedInstanceを返します。

編集:

+ allocWithZoneで保持する理由:?

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

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

マイクアッシュのサイト

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

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

クラスメソッドで self を参照しています!ビッグノーノー!次に、 [[self alloc] init] を呼び出して、インスタンスを破棄します。私が推測しているように、 init ではなく、クラスメソッドでシングルトン参照を割り当てる必要があります。次に、 _sharedMyClass がゼロに初期化されるという本当の保証はありません。明示的に nil に初期化する必要があります。

static MyClass *_sharedMyClass = nil;

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

おそらくこれもそこにあったでしょう...

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

initに保存しなかったのは、allocが呼び出したメソッドに保存していたためです。これは、Appleが例で示しているパターンです。 initにも値を保存すると、すべてが正常になり、警告は消えます。 allocWithZoneの実装はそのままにしておきます。

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top