views:

918

answers:

4

I'm using the singleton pattern in several places in an application, and I'm getting memory leak errors from clang when analyzing the code.

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)

I'm using these settings for scan-build:

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

I'm fairly certain that the code in the singleton is just fine - after all, it's the same code referenced here on Stack Overflow as well as in Apple's documentation - but I would like to get the memory leak warning sorted out so my scan-build returns success.

+3  A: 

I may be being exceptionally dense, but surely your line 5

[[self alloc] init];

allocates an object of the containing class type, and promptly throws it away? Do you not want

_sharedMyClass = [[self alloc] init];

?

Adam Wright
Actually the `-init` method sets the static _sharedMyClass variable, so this is fine. See: http://stackoverflow.com/questions/145154/what-does-your-objective-c-singleton-look-like/145221#145221
pix0r
Then I would guess that CLANG isn't smart enough to infer that the [self alloc] return is actually setting the self parameter, which will be saved by the init method. It thus thinks it's just lost (as I did, without more context).
Adam Wright
LOL, you are correct. Thanks.
pix0r
A: 

You also probably had this in there too...

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

The reason you weren't storing it in init is because you were storing it in the method that alloc called. This is the pattern Apple has in their examples. If you save the value in your init as well, all is fine and the warning goes away. I'd leave the allocWithZone implementation alone.

Shawn
+2  A: 

You may be interested in a simple, one-method, GCD-based singleton implementation (and thus 10.6+ only) posted on Mike Ash's site:

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

    dispatch_once(&pred, ^{ foo = [[self alloc] init]; });
    return foo;
}
Preston
Thanks, looks nice.. Unfortunately I'm actually on the iPhone platform so that's not an option yet.
pix0r
+1  A: 

Apple has since updated their recommended singleton code to pass the static analyzer:

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

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

Now +sharedManager calls super's -allocWithZone: and assigns the return of -init, and the singleton's -allocWithZone: just returns a retained sharedInstance.

Justin Anderson