views:

118

answers:

1

Here are two methods that return a dictionary of my custom four-propery objects. They make arrays of strings, floats and BOOLs to put in the Chemical objects, then build a dictionary from the arrays. I'm new enough to the whole memory management game that I'm not always sure when I own something and when to release it. I'm making all kinds of strings on the fly.

Here's the thing: The static analyzer sees no problem with the first method, - (id)generateChlorineDictionary but says there's a leak in the second one, - (id)generateCYADictionary. It says it starts at the NSMutableArray *cyaGranulesArray... and then goes to NSDictionary *cyaDictionary... and finally to the return cyaDictionary statement.

Here are the two methods; sorry they're so long!


EDIT: Changed name from generateChlorineDictionary to newChlorineDictionary
Removed a release that happened after the return

- (id)newChlorineDictionary {
// Sets up the array for the Bleach key
    NSMutableArray *bleachArray = [[NSMutableArray alloc] init]; 
    NSArray *bleachConcentrationArray = [[NSArray alloc] initWithObjects:@"6%", @"10%", @"12%", nil];
    float bleachConstantArray[] = {0.0021400, 0.0012840, 0.0010700};
    for (int i=0; i<3; i++) {
        Chemical *bleachChemical = [[Chemical alloc] initWithChemical:@"Bleach" 
                                                andConcentration:[bleachConcentrationArray objectAtIndex:i] 
                                                     andConstant:bleachConstantArray[i] 
                                                     andIsLiquid:YES];
        [bleachArray addObject:bleachChemical];
        NSLog(@"bleachChemical: chemName = %@, chemConcentration = %@, chemConstant = %1.6f, chemIsLiquid = %d", bleachChemical.chemName, bleachChemical.chemConcentration, bleachChemical.chemConstant, bleachChemical.chemIsLiquid);
        [bleachChemical release];
    }
    bleachConcentrationArray = nil;
// Sets up the array for the Trichlor key   
    NSMutableArray *trichlorArray = [[NSMutableArray alloc] init];
    Chemical *trichlorChemical = [[Chemical alloc] initWithChemical:@"Trichlor" 
                                            andConcentration:@"90%" 
                                                 andConstant:0.0001480
                                                 andIsLiquid:NO];
    [trichlorArray addObject:trichlorChemical];
    NSLog(@"trichlorChemical: chemName = %@, chemConcentration = %@, chemConstant = %1.6f, chemIsLiquid = %d", trichlorChemical.chemName, trichlorChemical.chemConcentration, trichlorChemical.chemConstant, trichlorChemical.chemIsLiquid);
    [trichlorChemical release];
// Sets up the array for the Dichlor key
    NSMutableArray *dichlorArray = [[NSMutableArray alloc] init]; 
    NSArray *dichlorConcentrationArray = [[NSArray alloc] initWithObjects:@"56%", @"62%", nil];
    float dichlorConstantArray[] = {0.0002400, 0.0002168};
    for (int i=0; i<2; i++) {
        Chemical *dichlorChemical = [[Chemical alloc] initWithChemical:@"Dichlor" 
                                                andConcentration:[dichlorConcentrationArray objectAtIndex:i] 
                                                     andConstant:dichlorConstantArray[i] 
                                                     andIsLiquid:NO];
        [dichlorArray addObject:dichlorChemical];
        NSLog(@"dichlorChemical: chemName = %@, chemConcentration = %@, chemConstant = %1.6f, chemIsLiquid = %d", dichlorChemical.chemName, dichlorChemical.chemConcentration, dichlorChemical.chemConstant, dichlorChemical.chemIsLiquid);
        [dichlorChemical release];
    }
    dichlorConcentrationArray = nil;
// Sets up the array for the Cal Hypo key
    NSMutableArray *calHypoArray = [[NSMutableArray alloc] init]; 
    NSArray *calHypoConcentrationArray = [[NSArray alloc] initWithObjects:@"48%", @"53%", @"65", @"73", nil];
    float calHypoConstantArray[] = {0.0002817, 0.0002551, 0.0002080, 0.0001852};
    for (int i=0; i<2; i++) {
        Chemical *calHypoChemical = [[Chemical alloc] initWithChemical:@"Cal Hypo" 
                                                andConcentration:[calHypoConcentrationArray objectAtIndex:i] 
                                                     andConstant:calHypoConstantArray[i] 
                                                     andIsLiquid:NO];
        [calHypoArray addObject:calHypoChemical];
        NSLog(@"calHypoChemical: chemName = %@, chemConcentration = %@, chemConstant = %1.6f, chemIsLiquid = %d", calHypoChemical.chemName, calHypoChemical.chemConcentration, calHypoChemical.chemConstant, calHypoChemical.chemIsLiquid);
        [calHypoChemical release];
    }
    calHypoConcentrationArray = nil;
// Sets up the array for the Li Hypo key    
    NSMutableArray *liHypoArray = [[NSMutableArray alloc] init];
    Chemical *liHypoChemical = [[Chemical alloc] initWithChemical:@"Li Hypo" 
                                            andConcentration:@"90%" 
                                                 andConstant:0.0003800
                                                 andIsLiquid:NO];
    [liHypoArray addObject:liHypoChemical];
    NSLog(@"liHypoChemical: chemName = %@, chemConcentration = %@, chemConstant = %1.6f, chemIsLiquid = %d", liHypoChemical.chemName, liHypoChemical.chemConcentration, liHypoChemical.chemConstant, liHypoChemical.chemIsLiquid);
    [liHypoChemical release];
// The array of keys for the chlorine chemicals
    NSArray *chlorineKeys = [[NSArray alloc] initWithObjects:@"Bleach", @"Trichlor", @"Dichlor", @"Cal Hypo", @"Li Hypo", nil];
// The array of values for the chlorine chemicals
    NSArray *chlorineValues = [[NSArray alloc] initWithObjects:bleachArray, trichlorArray, dichlorArray, calHypoArray, liHypoArray, nil];
    [bleachArray release];
    [trichlorArray release];
    [dichlorArray release];
    [calHypoArray release];
    [liHypoArray release];
// The dictionary to hold the arrays of chlorine chemical objects
    NSDictionary *chlorineDictionary = [[NSDictionary alloc] initWithObjects:chlorineValues forKeys:chlorineKeys];
    [chlorineValues release];
    [chlorineKeys release];
    return chlorineDictionary;
}

EDIT: Changed name from generateCYADictionary to newCYADictionary
Removed a release that happened after the return

- (id)newCYADictionary {
    // Sets up the array for the CYA Granules key   
    NSMutableArray *cyaGranulesArray = [[NSMutableArray alloc] init];
    Chemical *cyaGranulesChemical = [[Chemical alloc] initWithChemical:@"CYA Granules" 
                                                   andConcentration:@"" 
                                                        andConstant:0.0001330
                                                        andIsLiquid:NO];
    [cyaGranulesArray addObject:cyaGranulesChemical];
    NSLog(@"cyaGranulesChemical: chemName = %@, chemConcentration = %@, chemConstant = %1.6f, chemIsLiquid = %d", cyaGranulesChemical.chemName, cyaGranulesChemical.chemConcentration, cyaGranulesChemical.chemConstant, cyaGranulesChemical.chemIsLiquid);
    [cyaGranulesChemical release];
    // Sets up the array for the Liquid Stabilizer key  
    NSMutableArray *liquidStabilizerArray = [[NSMutableArray alloc] init];
    Chemical *liquidStabilizerChemical = [[Chemical alloc] initWithChemical:@"Liquid Stabilizer" 
                                                 andConcentration:@"" 
                                                      andConstant:0.0003460
                                                      andIsLiquid:YES];
    [liquidStabilizerArray addObject:liquidStabilizerChemical];
    NSLog(@"liquidStabilizerChemical: chemName = %@, chemConcentration = %@, chemConstant = %1.6f, chemIsLiquid = %d", liquidStabilizerChemical.chemName, liquidStabilizerChemical.chemConcentration, liquidStabilizerChemical.chemConstant, liquidStabilizerChemical.chemIsLiquid);
    [liquidStabilizerChemical release];
    // The array of keys for the CYA chemicals
    NSArray *cyaKeys = [[NSArray alloc] initWithObjects:@"CYA Granules", @"Liquid Stabilizer", nil];
    // The array of values for the CYA chemicals
    NSArray *cyaValues = [[NSArray alloc] initWithObjects:cyaGranulesArray, liquidStabilizerArray, nil];
    [cyaGranulesArray release];
    [liquidStabilizerArray release];
    // The dictionary to hold the arrays of CYA chemical objects
    NSDictionary *cyaDictionary = [[NSDictionary alloc] initWithObjects:cyaValues forKeys:cyaKeys]; 
    [cyaKeys release];
    [cyaValues release];
    return cyaDictionary;
}
+2  A: 

Replace the

return cyaDictionary;
[cyaDictionary release];

With

return [cyaDictionary autorelease];



Or, you might replace the

NSDictionary *chlorineDictionary = [[NSDictionary alloc] initWithObjects:chlorineValues forKeys:chlorineKeys];

With

NSDictionary *chlorineDictionary = [NSDictionary dictionaryWithObjects:chlorineValues forKeys:chlorineKeys];

Instead of adding the autorelease.

In your original implementation the [cyaDictionary release]; is never executed (because it is after return).

You can use this dictionary outside this method and you shouldn't release it there.

You might want also return a retained object (without the autorelease) and release it outside the method. In this case you should start the method name with "new" or "alloc"...

EDIT (Important):
You should use only one of my suggestions.

  • Use the autorelease in the return line
  • Use the dictionaryWith...
  • Add the "new" or "alloc" prefix in the method name and release the returned object outside this method.

If you replace the alloc init with dictionaryWith... then you get an autoreleased object. And then, if you release it in the outer method then you have a serious problem (the object will try to release itself after the current runloop of the thread and it may crash the app because the object will already be released by you).

EDIT (due to one of the comments)
If you want to create a property that will return a dictionary:

// MyClass.h file
@interface MyClass : NSObject {

    ..

    NSDictionary *_dict1;

    ..

}

@property (nonatomic, retain) NSDictionary *dict1;

..

@end

// MyClass.m file

@implementation MyClass

@synthesize dict1 = _dict1;

..

- (NSDictionary *)dict1 {
    if (_dict1 == nil) {
        NSDictionary *dict1Temp = [NSDictionary new];

        // Your implementation goes here...

        self.dict1 = dict1Temp;
        [dict1Temp release];
    }
}

..

- (void)dealloc {
    [_dict1 release];
    [suoer dealloc];
}

@end
Michael Kessler
Thanks Michael, I changed the method names to `- (id)newChlorineDictionary` for example. In the method that I call those, I was already releasing the returned dictionary after setting it as a property in the next view controller anyway. That seems to satisfy the static analyzer.
Steve
I tried your second suggestion - removing the `alloc` but I get an error about not responding to some class method - too tired to redo it and write down the error now... But I didn't think you could leave out the `alloc` when you made a new object. Anyway, it seems to build ok now - I'll run with Instruments and see what it says... Thanks again!
Steve
Alas, in Instruments I leak something every time I generate the new controllers in my table view. The leak happens in this class, although I guess it could be in another method. But the static analyzer doesn't catch anything. I'm also leaking when I present a picker view modally, but that's another post...
Steve
@Steve, you should use only one of my suggestions. If you replace the `alloc init` with `dictionaryWith...` then you get an autoreleased object. And then, if you release it in the outer method then you have a serious problem (the object will try to release itself after the current runloop of the thread and it may crash the app because the object will already be released by you).
Michael Kessler
@Michael, got it - I just have the new names (starting with "new") and got rid of the releases after the returns. I have to generate about 7 of these dictionaries. Could I declare some general purpose arrays as properties and reuse them? I tried using the same array names in the two methods and in the second one I'd get something like `redefining name` errors during compile. If I make a `chemicalSourceDictionary` as a property and `[[alloc] init]` it during, say, `viewDidLoad`, could I just use `[NSDictionary initWith...` in the methods and not release until `dealloc`?
Steve
@Steve, I have added another code sample for property... See if it is what you need.
Michael Kessler