我在应用程序的几个地方使用单例模式,在分析代码时,我从 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的文档中引用的代码相同 - 但我希望将内存泄漏警告排除在外我的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];
}

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

编辑:

为什么保留+ allocWithZone:?

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

对+ alloc的每次调用都应该使用-release或-autorelease进行平衡,因此如果不在+ allocWithZone:中保留,共享实例可能会从其他用户下解除分配。

您可能会对 Mike Ash的网站

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