代码之家  ›  专栏  ›  技术社区  ›  pix0r

目标C单件和LLVM/CLANG泄漏警告

  •  2
  • pix0r  · 技术社区  · 15 年前

    我在一个应用程序的几个地方使用了单例模式,并且从 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

    我相当肯定,单例中的代码是很好的——毕竟,它是在堆栈溢出和苹果文档中引用的相同代码——但是我想把内存泄漏警告整理出来,这样我的扫描构建就返回成功。

    5 回复  |  直到 12 年前
        1
  •  6
  •   Adam Wright    15 年前

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

    [[self alloc] init];
    

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

    _sharedMyClass = [[self alloc] init];
    

    ?

        2
  •  5
  •   Justin Anderson    14 年前

    苹果已经更新了他们的 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 和单人间的 -分配区域: 只返回保留的sharedInstance。

    编辑:

    为什么保留在+allocWithZone:?

    +allocWithZone:被重写,因为使用MyGizMoClass的人可以通过调用[[MyGizMoClass alloc]init]而不是[MyGizMoClass SharedManager]来绕过单例。它被保留是因为+alloc总是返回保留计数为+1的对象。

    对+alloc的每个调用都应该使用-release或-autorelease进行平衡,因此如果没有在+allocWithZone中保留,共享实例可能会从其他用户下释放出来。

        3
  •  4
  •   user155959    15 年前

    您可能对发布的基于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;
    }
    
        4
  •  1
  •   Relish    12 年前

    您正在引用 self 在类方法中!大不不!第二,你打电话来 [[self alloc] init] 把这个例子扔掉。您应该在class方法中分配singleton引用,而不是在 init 就像我猜你在做什么。其次,没有真正的保证 _sharedMyClass 将初始化为零。您应该显式地将其初始化为 nil .

    static MyClass *_sharedMyClass = nil;
    
    + (MyClass *)sharedMyClass {
      @synchronized(self) {
        if (_sharedMyClass == nil)
          _sharedMyClass = [[MyClass alloc] init];
      }
      return _sharedMyClass;
    }
    
        5
  •  0
  •   Shawn    15 年前

    你可能也有这个…

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

    您没有将它存储在in it中的原因是您将它存储在alloc调用的方法中。这就是苹果在其示例中的模式。如果您在init中也保存了这个值,那么一切都会好起来,警告就会消失。我将不使用allocWithZone实现。