2

我在应用程序的多个位置使用单例模式,并且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

我相当确定单例中的代码很好 - 毕竟,它与 Stack Overflow 以及 Apple 文档中引用的代码相同 - 但我想整理内存泄漏警告,所以我的扫描 -构建返回成功。

4

5 回答 5

6

我可能特别密集,但肯定是你的第 5 行

[[self alloc] init];

分配包含类类型的对象,并立即将其丢弃?你不想

_sharedMyClass = [[self alloc] init];

?

于 2009-05-06T16:16:13.157 回答
5

此后,Apple 更新了他们推荐的单例代码以通过静态分析器:

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

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

现在+sharedManager调用 super-allocWithZone:并分配 的返回值-init,而单例-allocWithZone:只返回一个保留的 sharedInstance。

编辑:

为什么要保留在 +allocWithZone: 中?

+allocWithZone: 被覆盖,因为使用 MyGizmoClass 的人可以通过调用 [[MyGizmoClass alloc] init] 而不是 [MyGizmoClass sharedManager] 来规避单例。它被保留是因为 +alloc 应该总是返回一个保留计数为 +1 的对象。

对 +alloc 的每次调用都应与 -release 或 -autorelease 保持平衡,因此如果 +allocWithZone: 中没有保留,则共享实例可能会从其他用户下释放。

于 2009-12-08T01:55:27.780 回答
4

您可能对发布在Mike Ash 的网站上的简单、单一方法、基于 GCD 的单例实现(因此仅限 10.6+)感兴趣:

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

    dispatch_once(&pred, ^{ foo = [[self alloc] init]; });
    return foo;
}
于 2009-11-12T22:15:34.843 回答
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;
}
于 2012-08-18T04:24:05.407 回答
0

你也可能在里面也有这个......

+ (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 调用的方法中。这是苹果在他们的例子中的模式。如果你也将值保存在你的 init 中,一切都很好,警告就会消失。我会单独留下 allocWithZone 实现。

于 2009-11-06T03:47:05.583 回答