Search code examples
objective-ciosxcode4clang-static-analyzer

How to make clear to Clang static analyzer when something's not leaking?


When using Clang static analyzer to analyze my Objective-C code for iOS, I get lots of "potential leaks". Many of the leaks make me wonder why it's erroneous at all. One example that left me wondering in particular was the following:

I have a class-variable of type NSDictionary, used for storing some settings. Now when I have a method to change something inside the dictionary:

- (void) loadPassengerCompartiments {
    NSMutableArray *paxCompartiments = [self.outputTable objectAtIndex:2];
    NSArray *paxCompSrc = [self.values objectForKey:@"PassengerCompartiments"];

    for(MassPerson *passenger in paxCompSrc) {
=>      [paxCompartiments addObject:[[PaxCompartimentOutputField alloc] initWithPerson:passenger]];
    }
}

Clang errs at the in-line allocation and direct association of PaxCompartimentOutputField. When running this code in instruments it doesn't leak.

Two ways I could come up with to solve this are:

  1. use autorelease
  2. replace the in-line allocation with the following code:

(code)

PaxCompartimentOutputField *field = [[PaxCompartimentOutputField alloc] initWithPerson:passenger];
[paxCompartiments addObject:field];
[field release];

The first option is obsolete imho (and especially on iOS its use is discouraged) The second option is pretty bulky - especially when creating an array with more objects (say 10 objects loading default settings).

I don't want to ignore Clang's warnings, as it is an excellent tool for finding bugs & leaks. What is the 'right' way to do this in Objective-C for those cases?


Solution

  • That is a leak.

    Since you alloc-init PaxCompartimentOutputField, you own it and you must relinquish ownership of it.

    You have 3 options (you already mentioned 2 of them):

    1) Use a convenience constructor, when one is available, or in a custom class, declare one. Convenience constructors return objects you do not own, typically by sending an autorelease message to the object they return. It would look like this:

    [paxCompartiments addObject:[PaxCompartimentOutputField paxCompartimentOutputWithPerson:passenger]];
    

    2) Use autorelease.

    [paxCompartiments addObject:[[[PaxCompartimentOutputField alloc] initWithPerson:passenger]] autorelease];
    

    3) Use a temporary variable.

    PaxCompartimentOutputField *tempField = [[PaxCompartimentOutputField alloc] initWithPerson:passenger];
    [paxCompartiments addObject:tempField];
    [tempField release];