I create a NSMutableString called mutableScoreHolder outside my do-while loop.
I alloc it, 'copy' it into an other object called a 'Match' that contains a NSString.
When I build and analyze the code it reports a potential memory leak of my mutableScoreHolder.
NSString * scoreHolder;
NSMutableString * mutableScoreHolder;
count = 1;
numMatchCounter = 0;
int startOfScoreIndex = 0;
int endOfScoreIndex = 0;
int numberOfGoals=0;
do
{
match = [substring rangeOfString: #"points_bg"];
if (match.location == NSNotFound)
{
break;
}
if ((match.location + match.length) > ([substring length]))
{
break;
}
substring = [substring substringFromIndex:(match.location + match.length)];
match2 = [substring rangeOfString: #">"];
startOfScoreIndex = match2.location+1;
match2 = [substring rangeOfString: #"<"];
endOfScoreIndex = match2.location;
if ((count % 2) == 1)
{
scoreHolder = [substring substringWithRange:NSMakeRange(startOfScoreIndex, (endOfScoreIndex-startOfScoreIndex))];
mutableScoreHolder = [[NSMutableString alloc] initWithString:scoreHolder];
numberOfGoals += [scoreHolder intValue];
}
else
{
Match *aMatch = [theMatchScoresArray objectAtIndex:(numMatchCounter)];
numberOfGoals += [[substring substringWithRange:NSMakeRange(startOfScoreIndex, (endOfScoreIndex-startOfScoreIndex))] intValue];
[scoreHolder stringByAppendingString:#" - "];
[mutableScoreHolder appendString:#" - "];
[scoreHolder stringByAppendingString:[substring substringWithRange:NSMakeRange(startOfScoreIndex, (endOfScoreIndex-startOfScoreIndex))]];
[mutableScoreHolder appendString:[substring substringWithRange:NSMakeRange(startOfScoreIndex, (endOfScoreIndex-startOfScoreIndex))]];
aMatch.theScore = [mutableScoreHolder copy];
aMatch.matchGoalCount = numberOfGoals;
numMatchCounter++;
numberOfGoals=0;
}
count++;
}
while ( match.length != 0 );
Here is my Match Object Class.
#interface Match : NSObject
{
NSString *teamName1;
NSString *teamName2;
NSString *theScore;
int matchGoalCount;
NSMutableArray *scorersArray;
NSMutableArray *scorerOrderArray;
NSString *matchStatus;
NSString *matchDate;
bool matchNotStarted;
}
#property(nonatomic) int matchGoalCount;
#property(nonatomic) bool matchNotStarted;
#property(nonatomic, retain) NSString *teamName1;
#property(nonatomic, retain) NSString *teamName2;
#property(nonatomic, retain) NSString *theScore;
#property(nonatomic, retain) NSString *matchStatus;
#property(nonatomic, retain) NSString *matchDate;
#property(nonatomic, retain) NSMutableArray *scorersArray;
#property(nonatomic, retain) NSMutableArray *scorerOrderArray;
-(id)init;
#end
#import "Match.h"
#implementation Match
#synthesize teamName1;
#synthesize teamName2;
#synthesize theScore;
#synthesize scorersArray;
#synthesize matchDate;
#synthesize matchStatus;
#synthesize matchGoalCount;
#synthesize scorerOrderArray;
#synthesize matchNotStarted;
-(id)init
{
//NSLog(#"****** MATCH INIT ******");
self = [super init];
scorersArray =[[NSMutableArray alloc] init];
scorerOrderArray =[[NSMutableArray alloc] init];
return self;
}
-(void) dealloc
{
[scorersArray release];
[scorerOrderArray release];
[teamName1 release];
[teamName2 release];
[theScore release];
[matchStatus release];
[matchDate release];
[scorersArray release];
[scorerOrderArray release];
[super dealloc];
}
#end
I do find there there is a string leaked when i run instruments checking for leaks. So I think this 'potential leak' might be the leak I see.
Because the scores has a retain
#property(nonatomic, retain) NSString *theScore;
And there is a string copied into it
aMatch.theScore = [mutableScoreHolder copy];
Could that give a retain count of 2? And so then leak?
Sorry for the complicated question! Has my head spinning trying to get my head around it.
Thanks
-Code
You're definitely leaking here, for 2 separate reasons.
The first is you're alloc/init'ing an NSMutableString and stuffing it into mutableScoreHolder. This is a local variable, and as soon as this value goes out of scope (or gets replaced the next time a new array is created) the old value is leaked. This should be an autoreleased value instead, as in [NSMutableString stringWithString:scoreHolder].
The second is you're copying the string and stuffing the resulting value into a property. What you should do is redeclare that property as copy
#property(nonatomic, copy) NSString *theScore;
and then just assign mutableScoreHolder to that property directly
aMatch.theScore = mutableScoreHolder
With your existing code, you copy the array, and then the property retains it. With this change, the property copies it directly, and no extra retains are used.
Note, in general it's a good idea to declare properties with supported types as copy. This includes things like NSString, NSArray, NSDictionary, etc. If you're assigning an already-immutable object to the property, the copy falls back to retain instead and there's no performance hit. But in situations like yours where you're assigning mutable objects, it will copy it as appropriate and keep an immutable snapshot in the property.
And there is a string copied into it
aMatch.theScore = [mutableScoreHolder copy];
Could that
give a retain count of 2? And so then
leak?
exactly. You could change the property of theScore from retain to copy to fix this. THen you can use aMatch.theScore = mutableScoreHolder;
and there is (at least) one other leak:
mutableScoreHolder = [[NSMutableString alloc] initWithString:scoreHolder];
this gets never released.
Related
I have recently inherited a large project using an sqlite3 database and currently I am muscling through a significant amount of memory leaks scattered throughout. A couple of the leaks have me confused and demoralised after not being able to solve them. This loop within a method leaks an awful amount of bytes but appears to be so simplistic I simply don't know how to change it to prevent the leak.
...
while ((ret=sqlite3_step(selStmt))==SQLITE_ROW)
{
GraphData *item = [GraphData alloc];
item.key = sqlite3_column_int(selStmt, 0);
item.value = [NSString stringWithUTF8String:(char *)sqlite3_column_text(selStmt,1)];
[newData addObject:item];
[item release], item = nil;
}
...
#interface GraphData : NSObject{
NSInteger key;
NSString *value;
}
#property (nonatomic, readwrite) NSInteger key;
#property (nonatomic, retain) NSString *value;
-(id)initWithPrimaryKey:(NSInteger) xid;
-(id)initWithName:(NSString *)n key:(NSInteger)i;
#end
#import "GraphData.h"
#implementation GraphData
#synthesize key,value;
-(id)initWithPrimaryKey:(NSInteger) xid{
self.key = xid;
self.value = #"";
return self;
}
-(id)initWithName:(NSString *)n key:(NSInteger)i{
self.key = 0;
self.value = n;
return self;
}
#end
Almost all the leaks within this datasource class come from this loop.
I'm hoping this is something trivial that my inexperience has overlooked.
Thanks for taking the time to look at my question.
There's no dealloc in your GraphData class.
- (void)dealloc {
[value release];
[super dealloc];
}
(I assume you're not using ARC because you have release in your first code snippet. I really recommend converting to ARC - this kind of leak vanishes.)
I've made a container class to store a single tweet. Its initialized by passing in a dictionary object which is a single tweet.
I then store an array of these 'tweets' which I process through to display in a table.
The project is now finished and I am reviewing everything at the moment and I was wondering is there a better way to do this in the future. Is the memory handled correctly. I declare the string member vars with 'copy' and later in the dealloc I use a 'release' rather than just setting them to 'nil'.
Is my init ok or could that be improved?
Tweet.h
#import
#interface Tweet : NSObject
{
NSString * _userName;
NSString * _tweetText;
NSString * _tweetURL;
}
#property (nonatomic, copy) NSString * userName;
#property (nonatomic, copy) NSString * tweetText;
#property (nonatomic, copy) NSString * tweetURL;
- (id) initWithDict:(NSDictionary *)productsDictionary;
#end
Tweet.m
#implementation Tweet
#synthesize userName = _userName;
#synthesize tweetText = _tweetText;
#synthesize tweetURL = _tweetURL;
- (id) initWithDict:(NSDictionary *)productsDictionary
{
NSDictionary *aDict = [productsDictionary objectForKey:#"user"];
self.userName = [aDict objectForKey:#"screen_name"];
self.tweetText = [productsDictionary objectForKey:#"text"];
NSRange match;
match = [self.tweetText rangeOfString: #"http://"];
if (match.location != NSNotFound)
{
NSString *substring = [self.tweetText substringFromIndex:match.location];
NSRange match2 = [substring rangeOfString: #" "];
if (match2.location == NSNotFound)
{
self.tweetURL = substring;
}
else
{
self.tweetURL = [substring substringToIndex:match2.location];
}
}
else
{
self.tweetURL = nil;
}
return self;
}
-(void) dealloc
{
[self.tweetText release];
[self.tweetURL release];
[self.userName release];
[super dealloc];
}
#end
Many Thanks,
Code
At first sight, I see no inherent flaws here. That looks fine. I would prefer to do:
-(void) dealloc
{
[_tweetText release];
[_tweetURL release];
[_userName release];
[super dealloc];
}
But what you do is good as well.
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.
Here is an expanded version of one of the NSCFString.
And another image.
Image with the 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
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!
2. After clicking :)
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)
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.
I have an NSMutableArray defined as a property, synthesized and I have assigned a newly created instance of an NSMutableArray. But after this my application always crashes whenever I try adding an object to the NSMutableArray.
Page.h
#interface Page : NSObject
{
NSString *name;
UIImage *image;
NSMutableArray *questions;
}
#property (nonatomic, copy) NSString *name;
#property (nonatomic, retain) UIImage *image;
#property (nonatomic, copy) NSMutableArray *questions;
#end
Page.m
#implementation Page
#synthesize name, image, questions;
#end
Relevant code
Page *testPage = [[Page alloc] init];
testPage.image = [UIImage imageNamed:#"Cooperatief leren Veenman-11.jpg"];
testPage.name = [NSString stringWithString:#"Cooperatief leren Veenman-11.jpg"];
testPage.questions = [[NSMutableArray alloc] init];
[testPage.questions addObject:[NSNumber numberWithFloat:arc4random()]];
The debugger reveals that the moment I use testPage.questions = [[NSMutableArray alloc] init]; the type of testPage.questions changes from NSMutableArray* to __NSArrayL* (or __NSArrayI*, not sure). I suspect this to be the problem, but I find it extremely odd. Anyone know what's happening here?
The problem is that you've declared the property as copy. This means your setter is going to be implemented something like this:
- (void) setQuestions:(NSMutableArray *)array {
if (array != questions) {
[questions release];
questions = [array copy];
}
}
The kicker here is that if you -copy an array (whether immutable or mutable), you will always get an immutable NSArray.
So to fix this, change the property to be retain instead of copy, and also fix this memory leak:
testPage.questions = [[NSMutableArray alloc] init];
It should be:
testPage.questions = [NSMutableArray array];
#property (nonatomic, copy) This setter declaration "copy" probably cast to NSArray why not retain or assign? I would retain anyway
You can also create a mutable copy method like so:
- (void)setQuestions:(NSMutableArray *)newArray
{
if (questions != newArray)
{
[questions release];
questions = [newArray mutableCopy];
}
}
This is really twisting my mind… I'm trying to access an NSMutableArray in an IBAction which I defined in viewDidLoad. Unfortunately I keep getting a EXC_BAD_ACCESS.
I'm new to all this so I'd really appreciate some insight in what I'm doing wrong.
Below find the corresponding code excerpts.
CounterViewController.h:
#interface CounterViewController : UIViewController{
NSMutableArray *countHistoryArray;
}
#property(nonatomic, retain) NSMutableArray *countHistoryArray;
CounterViewController.m:
#implementation CounterViewController
#synthesize countHistoryArray;
- (void)viewDidLoad {
[super viewDidLoad];
//Fill array with some dummy data
self.countHistoryArray = [[NSMutableArray alloc] init];
NSDate *now = [[[NSDate alloc] init] autorelease];
CurrentCount *historicCount = [[[CurrentCount alloc]
initWithCount:[NSNumber numberWithInteger:22]
description:#"Testcount"
dateAndTime:now] autorelease];
[self.countHistoryArray addObject: historicCount];
//Do some logging - everything is working fine here!
NSLog(#"%#", [self.countHistoryArray description]);
}
//Later on we click on a button and want to use the array
- (IBAction)doSomeStuff {
//Let's look at the array again - and now it crashes with EXC_BAD_ACCESS
NSLog(#"%#", [self.countHistoryArray description]);
}
Thanks a lot!
Manuel
EDIT Additional code as asked for by #jamapag
CurrentCount.h
#import <Foundation/Foundation.h>
#interface CurrentCount : NSObject {
NSNumber *counterLevel;
NSString *description;
NSDate *dateAndTime;
}
- (id)initWithCount:(NSNumber *)newCounterLevel description:(NSString *)newDescription dateAndTime:(NSDate *)newDateAndTime;
#property(nonatomic, copy) NSNumber *counterLevel;
#property(nonatomic, copy) NSString *description;
#property(nonatomic, copy) NSDate *dateAndTime;
#end
CurrentCount.m
#import "CurrentCount.h"
#implementation CurrentCount
#synthesize counterLevel;
#synthesize description;
#synthesize dateAndTime;
- (id)initWithCount:(NSNumber *)newCounterLevel description:(NSString *)newDescription dateAndTime:(NSDate *)newDateAndTime{
self = [super init];
if(nil != self){
self.counterLevel = newCounterLevel;
self.description = newDescription;
self.dateAndTime = newDateAndTime;
}
return self;
}
-(void) dealloc{
self.counterLevel = nil;
self.description = nil;
self.dateAndTime = nil;
[super dealloc];
}
#end
Are you sure that your code actually looks like this?
- (IBAction)doSomeStuff {
//Let's look at the array again - and now it crashes with EXC_BAD_ACCESS
NSLog(#"%#", [self.countHistoryArray description]);
}
Your question title says "NSMutableArray count causes EXC_BAD_ACCESS" - if that line of code actually says NSLog(#"%#", [self.countHistoryArray count]);, you'll almost certainly get a crash, since NSLog will attempt to treat a primitive type (the type returned by -[NSArray count]) as an object. In order to use -[NSArray count] in NSLog, use %u instead of %#:
- (IBAction)doSomeStuff {
// This time it should work!
NSLog(#"Array Count = %u", [self.countHistoryArray count]);
}
Remove autorelease from:
currentCount *historicCount = [[[CurrentCount alloc]
initWithCount:[NSNumber numberWithInteger:22]
description:#"Testcount"
dateAndTime:now] autorelease];
It looks like you are accidentally releasing countHistoryArray somewhere. Try removing all calls to it except for those two you showed. Additionally you can try enabling zombies to debug the problem.
Oh and by the way you probably don't really want a public NSMutableArray property and if you do you probably want it to be copy, not retain. Otherwise incapsulation kinda goes down the drain.
I know this question has already been solved and accepted but its for others who are or will face this issue.
I was facing the same issue, I tried all solutions but no solution worked for me. The project I am working on is NON-ARC.
I tried and made a simple change in property
Previously my property for NSMUTABLEARRAY was
#property (nonatomic, assign) NSMutableArray * dataArray;
I changed it to:
#property (nonatomic, retain) NSMutableArray * dataArray;
Changed it from ASSIGN to RETAIN
And it solved my problem