1

I am currently building an app for the iPhone and cannot figure out why I keep getting a memory leak to appear in the Leaks Instrument tool.

Here is the code and I have added comments to two places of where it is happening.

NSString *pathname = [[NSBundle mainBundle]  pathForResource:self.toUseFile ofType:@"txt" inDirectory:@"/"];
    //Line below causes a leak
    self.rawCrayons = [[NSString stringWithContentsOfFile:pathname encoding:NSUTF8StringEncoding error:nil] componentsSeparatedByString:@"\n"];

    self.sectionArray = [NSMutableArray array];
    for (int i = 0; i < 26; i++) [self.sectionArray addObject:[NSMutableArray array]];


    for(int i=0; i<self.rawCrayons.count; i++)
    {
        self.string = [self.rawCrayons objectAtIndex:i];
        NSUInteger firstLetter = [ALPHA rangeOfString:[string substringToIndex:1]].location;
        if (firstLetter != NSNotFound)
        {
            NSInteger audio = AUDIONUM(self.string);
            NSInteger pictures = PICTURESNUM(self.string);
            NSInteger videos = VIDEOSNUM(self.string);
            //Line below causes a leak
            [[self.sectionArray objectAtIndex:firstLetter] addObject:[[Term alloc] initToCall:NAME(self.string):audio:pictures:videos]];
        }

        [self.string release];
    }

Thanks in advance!

Edit

Here are my property declarations.

@property (nonatomic, retain) NSArray *filteredArray;
@property (nonatomic, retain) NSMutableArray *sectionArray;
@property (nonatomic, retain) UISearchBar *searchBar;
@property (nonatomic, retain) UISearchDisplayController *searchDC;
@property (nonatomic, retain) NSString *toUseFile;
@property (nonatomic, retain) NSArray *rawCrayons;
@property (nonatomic, retain) NSString *string;

@property (nonatomic, retain) TermViewController *childController;

Here are the leaks that are occurring after follow Nick Weaver's fixes. Memory Leaks

Here is an expanded version of one of the NSCFString. Memory Leaks in Depth

And another image. Another one in depth

Image with the Responsible Caller: Responsible Caller

Also, because this may be useful, here are the properties for Term:

@property (nonatomic, retain) NSString *name;
@property (nonatomic) NSInteger numberAudio;
@property (nonatomic) NSInteger numberPictures;
@property (nonatomic) NSInteger numberVideos;

And the implementation:

@implementation Term

@synthesize name, numberAudio, numberPictures, numberVideos;

- (Term*)initToCall:(NSString*) toSetName:(NSInteger) audio:(NSInteger) pictures:(NSInteger) videos
{
    self.name = [toSetName retain];
    self.numberAudio = audio;
    self.numberPictures = pictures;
    self.numberVideos = videos;

    return self;
}

- (NSString*)getName
{
    return [[name retain] autorelease];
}

-(void)dealloc
{
    [name release];
    [super dealloc];
}

@end
Flipper
  • 2,589
  • 3
  • 24
  • 32

3 Answers3

2

Ok, try this changed Version of Temp. I've deleted the getter because you have already one by synthesizing. You cann use the getter like this for name:

term.name

The problem was how you set the name: you want a copy of the name and setting it with the synthesized setter without calling a retain should do the trick. You could, of course, have set it with the retained property of name but you should have left out retain, like this self.name = toSetName;. The setter will retain it for you.

@property (nonatomic, copy) NSString *name;
@property (nonatomic) NSInteger numberAudio;
@property (nonatomic) NSInteger numberPictures;
@property (nonatomic) NSInteger numberVideos;


@implementation Term

@synthesize name, numberAudio, numberPictures, numberVideos;

- (Term*)initToCall:(NSString*) toSetName:(NSInteger) audio:(NSInteger) pictures:(NSInteger) videos
{
    self.name = toSetName;
    self.numberAudio = audio;
    self.numberPictures = pictures;
    self.numberVideos = videos;

    return self;
}

-(void)dealloc
{
    [name release];
    [super dealloc];
}

Adding an object to an array will retain the instance, so the retain is 2 because you call

[[Term alloc] initToCall..

Do something like

Term *term = [[Term alloc] initToCall..];

[theArray addObject:term];

[term release];

1. See the arrow in the first line in the address column? Click it! enter image description here

2. After clicking :) enter image description here

Nick Weaver
  • 47,228
  • 12
  • 98
  • 108
  • @Nick Weaver I followed this and am now getting a leak at NSCFString which when I double click it, it's all just Assembly Code. – Flipper Apr 03 '11 at 21:36
  • please show us the declaration of your properties in your header file. – Nick Weaver Apr 03 '11 at 21:44
  • @Nick Weaver I have added my property declarations above. – Flipper Apr 03 '11 at 22:01
  • 1
    First of all, change your NSString properties from retain to copy: NSStrings are immutable so you want a copy of it not something that can change in the background by some process. – Nick Weaver Apr 04 '11 at 07:31
  • @Nick Weaver Should that be done throughout the entire app for all NSStrings? – Flipper Apr 04 '11 at 14:23
  • 1
    My opinion is: yes, I do it that way. If you don't store whole books in the strings, copy is the right choice for NSStrings. Have a look at [NSString property copy or retain](http://stackoverflow.com/questions/387959/nsstring-property-copy-or-retain), this is explaining the difference very well. – Nick Weaver Apr 04 '11 at 14:25
  • Ok thanks. And this should fix the memory leak at NSCFString? – Flipper Apr 04 '11 at 14:28
  • 1
    I can't tell, you have to find out. The problem solving here is iterative, I don't have the running code, so I can only help with the next step depending on the given information. – Nick Weaver Apr 04 '11 at 14:50
  • @Nick Weaver I am not getting less leaks and I have posted an image in my original post of the two leaks that I am getting. – Flipper Apr 04 '11 at 17:57
  • @Flipper can you expand one of the two items and dive into one line by doubleclicking it? Then post a screenshot, please. The last column should be titled Responsible Caller. – Nick Weaver Apr 04 '11 at 22:30
  • @Nick Weaver I have added two images because I could not find where Responsible Caller was. – Flipper Apr 05 '11 at 01:58
  • Look at the two screenshots I added to my answer. The last one you get by clicking the arrow, or double click. – Nick Weaver Apr 05 '11 at 07:45
  • I have added the other screenshot as well as some more code that seems like it may be relevant (after looking at my screenshot). – Flipper Apr 05 '11 at 13:45
  • 1
    Thanks for the new code, I think we are close to a solution. Look at my answer update. – Nick Weaver Apr 05 '11 at 15:44
  • I have tried over and over to get a memory leak and have been unsuccessful in my attempts therefore making your answer right and have fixed all of my problems. Thank you SO SO SO much! =) – Flipper Apr 07 '11 at 03:36
1

Hard to tell you why the first one is leaking, because we don't know what the property is declared as. Is it retain? copy? assign? what?

The last one is fairly self explanatory though, you're taking ownership of a Term object, and not releasing it when it's added. addObject: retains its argument, meaning if you don't need that Term anymore, you need to give up ownership. I.e., pass -autorelease to the result of your initToCall:::: (which btw is a very bad name for a method)

jer
  • 20,094
  • 5
  • 45
  • 69
1

Change:

[[self.sectionArray objectAtIndex:firstLetter] addObject:[[Term alloc] initToCall:NAME(self.string):audio:pictures:videos]];

to:

Term *tempTerm = [[Term alloc] initToCall:NAME(self.string):audio:pictures:videos];
[[self.sectionArray objectAtIndex:firstLetter] addObject:tempTerm];
[tempTerm release];

By alloc'ing an object you are responsible for it's release.

Joe
  • 3,664
  • 25
  • 27