Need help in finding a memory leak in copying Sqlite Database to Mutable array - objective-c

I seem to be having a 1.19 KB leak somewhere in the following code. opening up the call tree in instruments, I have narrowed down the leaks to the following:
61.8% of the leaks are coming from +[NSString stringWithUTF8String:]
38.1% of the leaks are coming from +[NSNumber numberWithDouble:]
-(void) readMinesFromDatabase
{
NSLog(#" Setup the database object");
sqlite3 *database;
// Init the Array
northernMines = [[NSMutableArray alloc] init];
nCentralMines = [[NSMutableArray alloc] init];
centralMines = [[NSMutableArray alloc] init];
southernMines = [[NSMutableArray alloc] init];
//NSLog(#" pre if statement");
// Open the database from the users filessytem
if(sqlite3_open([databasePath UTF8String], &database) == SQLITE_OK)
{
// Setup the SQL Statement and compile it for faster access
const char *sqlStatement = "SELECT * FROM mines";
//NSLog(#"pre 2nd if statement");
sqlite3_stmt *compiledStatement;
if(sqlite3_prepare_v2(database, sqlStatement, -1, &compiledStatement, NULL) != SQLITE_OK)
{
NSLog( #"Error: Failed to prepare stmt with message %s", sqlite3_errmsg(database));
}
else
{
// Loop through the results and add them to the feeds array
NSLog(#"pre loop");
while(sqlite3_step(compiledStatement) == SQLITE_ROW) {
NSString *name;
NSString *com;
NSString *county;
NSNumber *lat;
NSNumber *longit;
// Read the data from the result row
//deals with null strings in the name and commodity fields of the database
//NSLog(#"ered the loop");
if (sqlite3_column_text(compiledStatement, 3) != NULL) {
name = [NSString stringWithUTF8String:(char *)sqlite3_column_text(compiledStatement, 3)];
}
else {
name = #"";
}
if (sqlite3_column_text(compiledStatement, 10) != NULL) {
com = [NSString stringWithUTF8String:(char *)sqlite3_column_text(compiledStatement, 10)];
}
else {
com = #"";
}
//latitude and longitudes
lat = [NSNumber numberWithDouble:(double )sqlite3_column_double(compiledStatement, 4)];
longit = [NSNumber numberWithDouble:(double )sqlite3_column_double(compiledStatement, 5)];
//NSLog(#"long %#",longit);
// Create a new object with the data from the database
Mine *mine = [[Mine alloc] initWithMineName:name latitudeInitial:lat longitudeInitial:longit commodity:com];
// Add the object to the animals Array
county = [NSString stringWithUTF8String:(char *)sqlite3_column_text(compiledStatement, 8)];
if([county isEqualToString:#"Butte"] || [county isEqualToString:#"Plumas"] || [county isEqualToString:#"Yuba"] || [county isEqualToString:#"Sierra"])
{
[northernMines addObject:mine];
}
else if([county isEqualToString:#"Nevada" ]|| [county isEqualToString:#"Placer"] || [county isEqualToString:#"El Dorado"] || [county isEqualToString:#"Sutter"])
{
[nCentralMines addObject:mine];
}
else if([county isEqualToString:#"Amador"] || [county isEqualToString:#"Sacramento"] || [county isEqualToString:#"Calaveras"] || [county isEqualToString:#"San Joaquin"] || [county isEqualToString:#"Stanislaus"])
{
[centralMines addObject:mine];
}
else if([county isEqualToString:#"Tuolumne"] ||[county isEqualToString:#"Mariposa"] || [county isEqualToString:#"Madera"] || [county isEqualToString:#"Merced"])
{
[southernMines addObject:mine];
}
else
{
}
[mine release];
//[name release];
//[com release];
//[county release];
//[lat release];
//[longit release];
}
NSLog(#"done with loop");
//[mines addObject:#"nil"];
}
// Release the compiled statement from memory
sqlite3_finalize(compiledStatement);
}
sqlite3_close(database);
}
the mine object implementation file is:
#import "Mine.h"
#implementation Mine
#synthesize mineName, latitudeInitial, longitudeInitial, commodity;
-(id)initWithMineName:(NSString *)n latitudeInitial:(NSNumber *)l longitudeInitial:(NSNumber *)g commodity:(NSString *)c
{
self.mineName = n;
self.latitudeInitial = l;
self.longitudeInitial = g;
self.commodity = c;
return self;
}
#end

Well, it is a little hard to tell because of the indentation where the function ends (is this the entire function?) but I believe you are forgetting to release the 4 arrays that you are allocating at the beginning of the function. By leaking the arrays you are also leaking their contents.
Edit - for the initializer code you added:
I am not sure if this has anything to do with the memory leaks but I noticed that the initializer is not implemented correctly:
you should call init on super and update self (more details here)
of this I am not 100% sure, but I think you should not send messages (call methods) to the current object from inside the init method, and by using the dot notation you are actually calling the setter methods (but again, I am not really sure of this one)
I suppose that the properties are declared as (retain) and that you release them in the dealloc method. Which would be correct.
I can't really see anything else wrong with the code you showed. Maybe the problem is not exactly here (just an idea).
Edit 2 - example initializer
You should spend some time reading the Apple documentation, it has plenty of examples.
Here is my version (I hope it doesn't have too many mistakes):
-(id)initWithMineName:(NSString *)n latitudeInitial:(NSNumber *)l longitudeInitial:(NSNumber *)g commodity:(NSString *)c {
// Assign self to value returned by super's designated initializer
// Designated initializer for NSObject is init
self = [super init];
if (self) {
mineName = [n retain];
latitudeInitial = [l retain];
longitudeInitial = [g retain];
commodity = [c retain];
}
return self;
}

Just a thought, but could the memory leak be in the Mine object initializer ( -[Mine initWithMineName: latitudeInitial: longitudeInitial: commodity:] ) ??

Related

Remove object from an array stored in a singleton

Im working with a singleton to store some data, her's the implementation
static ApplicationData *sharedData = nil;
#implementation ApplicationData
#synthesize list;
+ (id)sharedData
{
static dispatch_once_t dis;
dispatch_once(&dis, ^{
if (sharedData == nil) sharedData = [[self alloc] init];
});
return sharedData;
}
- (id)init
{
if (self = [super init])
{
list = [[NSMutableArray alloc]init];
}
return self;
}
if list have less than 3 (2<) object i the app crash with "index 0 beyond bounds for empty array"
// NSMutableArray *anArray = [[NSMutableArray alloc]initWithObjects:#"", nil];
while ([[[ApplicationData sharedData]list] lastObject] != nil)
{
File *file = [[[ApplicationData sharedData]list] lastObject];
BOOL isDir;
if (![[NSFileManager defaultManager] fileExistsAtPath:file.filePath isDirectory:&isDir])
{
NSMutableDictionary *tmpDic = [NSMutableDictionary dictionaryWithObjects:[NSArray arrayWithObjects:file.fileName,file.filePath,logEnteryErrorfileNotFoundDisplayName,[formatter stringFromDate:[NSDate date]], nil] forKeys:[NSArray arrayWithObjects:logShredFileName,logShredFilePath,logShredStatue,logShredDate, nil]];
[logArray addObject:tmpDic];
errorOccured = YES;
[[[ApplicationData sharedData]list] removeLastObject];
continue;
}
... other code
}
if i use the anArray that work perfectly.
what is the problem ?
That's totally weird, you've probably did something else to achieve this. Why don't you use - (void)removeAllObjects?
Maybe you remove objects in the while cycle the last line, ie:
while ([[[ApplicationData sharedData]list] count] != 0)
{
// remove object from list
// ...
[[[ApplicationData sharedData]list] removeLastObject];
}
And just a note, you don't need to check if (sharedData == nil) in sharedData as far as it's guaranteed to be executed only once. (unless you do something outside to your static variable, but that's not how it's supposed to be done I believe)

Issue with NSString memory management

I have developed in C but am quite new to Objective-C and iPhone app development. I am working on an app that needs to strip the punctuation off a string. The function works but when I analyse the code it flags up some issues around one of the NSstrings I am using.
I don't understand why and therefore don't know how to fix it.
The code for the main function along with the analyser warning is:
- (IBAction)doIt {
NSString *start_punct = [[NSString alloc] init];
NSString *end_punct = [[NSString alloc] init];
NSString *actual_word = [[NSString alloc] init];
outputTextTextView.text = translatedText; //potential leak of an object alloctated on line xx and stored into 'actual word'
[translatedText release]; translatedText = nil;
[start_punct release]; start_punct = nil; //incorrect decrement of reference count of an object that is not owned at this point by the caller
[end_punct release]; end_punct = nil;
[actual_word release]; actual_word = nil; //this causes a crash
start_punct = [MainViewController getStartPunct:word start:&start_range_start len:&start_range_len];
end_punct = [MainViewController getEndPunct:word start:&end_range_start len:&end_range_len];
actual_word = [word substringWithRange: NSMakeRange(start_range_start,(end_range_start-start_range_start)+1)];
}
The code for the getStartPunct and getEndPunct functions is below
+(NSString*) getStartPunct:(NSString*) inputString
start:(NSInteger*)rangeStart
len:(NSInteger*)length {
NSString* start_str = nil;
NSRange firstAlphanumCharFromStart = [inputString rangeOfCharacterFromSet:[NSCharacterSet alphanumericCharacterSet]];
if (firstAlphanumCharFromStart.location != NSNotFound) {
start_str = [inputString substringWithRange: NSMakeRange(0, firstAlphanumCharFromStart.location)];
*length = firstAlphanumCharFromStart.length;
*rangeStart = firstAlphanumCharFromStart.location;
} //if
if (start_str == nil) {
*length=0;
*rangeStart=0;
}
return start_str;
} //getStartPunct
+(NSString*) getEndPunct:(NSString*) inputString
start:(NSInteger*)rangeStart
len:(NSInteger*)length {
NSString* end_str = nil;
NSInteger rnge = inputString.length;
NSCharacterSet* CS = [NSCharacterSet alphanumericCharacterSet];
NSRange firstNonAlphanumCharFromEnd = [inputString rangeOfCharacterFromSet:CS options:NSBackwardsSearch];
if (firstNonAlphanumCharFromEnd.location != NSNotFound) {
end_str = [inputString substringWithRange: NSMakeRange(firstNonAlphanumCharFromEnd.location+1, rnge - firstNonAlphanumCharFromEnd.location-1)];
*length = firstNonAlphanumCharFromEnd.length;
*rangeStart = firstNonAlphanumCharFromEnd.location;
} //if
if (end_str == nil) {
*length=0;
*rangeStart=0;
}
return end_str;
} //getEndPunct
Can someone see what the issue is? I'm sure it is something very basic..
Many Thanks in advance!
Thanks for all the responses so far.
adpalumbo you are right, I had paste the elements in the wrong order. The correct order is below and I have changed the initialization as suggested by Alex Nichol.
This has fixed 1 of the warning but the others (as shown below) still remain and I don't understand why 'start_punct' and 'end_punct' are behaving differently
- (IBAction)doIt {
NSString *start_punct = nil;
NSString *end_punct = nil;
NSString *actual_word = nil;
start_punct = [MainViewController getStartPunct:word start:&start_range_start len:&start_range_len]; // method returns objective with +0 retain count
end_punct = [MainViewController getEndPunct:word start:&end_range_start len:&end_range_len];
actual_word = [word substringWithRange: NSMakeRange(start_range_start,(end_range_start-start_range_start)+1)];
[translatedText release]; translatedText = nil;
[start_punct release]; start_punct = nil; //incorrect decrement of reference count
[end_punct release]; end_punct = nil;
//[actual_word release]; actual_word = nil; //possible abend
}

sqlite3 - stringWithUTF8String is leaking!

I would appreciate if someone could help me solve my leaking problem. The leaks occur at: aImage, aCategory, aDescription, category and categories. I release them in dealloc, but obviously that is not sufficient:
-(void) readListFromDatabase:(char *) sqlStatement {
// Setup some globals
databaseName = #"mydatabase.sql";
// Get the path to the documents directory and append the databaseName
NSArray *documentPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDir = [documentPaths objectAtIndex:0];
databasePath = [documentsDir stringByAppendingPathComponent:databaseName];
// Setup the database object
sqlite3 *database;
// Init the categories Array
categories = [[NSMutableArray alloc] init];
// Open the database from the users filessytem
if(sqlite3_open([databasePath UTF8String], &database) == SQLITE_OK) {
// Setup the SQL Statement and compile it for faster access
sqlite3_stmt *compiledStatement;
if(sqlite3_prepare_v2(database, sqlStatement, -1, &compiledStatement, NULL) == SQLITE_OK) {
// Loop through the results and add them to the feeds array
while(sqlite3_step(compiledStatement) == SQLITE_ROW) {
// Read the data from the result row
aImage = [NSString stringWithUTF8String:(char *)sqlite3_column_text(compiledStatement, 0)];
aCategory = [NSString stringWithUTF8String:(char *)sqlite3_column_text(compiledStatement, 1)];
aDescription = [NSString stringWithUTF8String:(char *)sqlite3_column_text(compiledStatement, 2)];
// Create a new category object with the data from the database
category=[[Category alloc] initWithName:aImage category_name:aCategory description_text:aDescription];
// Add the category object to the categories Array
[categories addObject:category];
[category release];
}
}
// Release the compiled statement from memory
sqlite3_finalize(compiledStatement);
}
sqlite3_close(database);
}
- (void)dealloc {
[databaseName release];
[databasePath release];
[categories release];
[aImage release];
[aCategory release];
[aDescription release];
[category release];
[super dealloc];
}
If the method is called multiple times, then the strings will leak because you need to release the previous values. You also overrelease the strings in dealloc because you never retained them. You should write the assignments like this:
[aImage release];
aImage = [[NSString stringWithUTF8String:(char *)sqlite3_column_text(compiledStatement, 0)] retain];
The only other way these strings could be leaking is if you are calling this method from a thread and you didn't create an auto release pool.
If the method is being called from a new thread, you need an autorelease pool:
- (void)myThreadFunction {
NSAutoReleasePool *pool = [[NSAutoReleasePool alloc] init];
try {
// ...
[self readListFromDatabase:whatever];
// ...
} #finally {
[pool release];
}
}
Is that method that you posted being called more than once on the same object? If it is, categories from the first call will leak because it is overwritten each time readListFromDatabase: is called. Try:
// Init the categories Array
[categories release];
categories = [[NSMutableArray alloc] init];
Why does the application terminate when I include [aImage autorelease] in the loop (it also terminates if [aImage release])?
A bit late, but I was updating an old project and came across a similar problem.
I had a convenience method that was incorrectly named!
- (NSString *)initStringFromPosition:(int)index {
char *str = (char *)sqlite3_column_text(init_statement, index);
return (str) ? [NSString stringWithUTF8String:str] : #"";
}
Analyse said I had a memory leak, but a simple rename to
- (NSString *)stringFromPosition:(int)index {
solved the problem

UITableView and SearchBar problem

I'm trying to add a Search bar to my UITableView. I followed this tutorial: http://clingingtoideas.blogspot.com/2010/02/uitableview-how-to-part-2-search.html.
I'm getting this error if I type a letter in the search box: Rooster(10787,0xa05ed4e0) malloc: *** error for object 0x3b5f160: double free
*** set a breakpoint in malloc_error_break to debug.
This error occurs here:
- (BOOL)searchDisplayController:(UISearchDisplayController *)controller shouldReloadTableForSearchString:(NSString *)searchString {
[self handleSearchForTerm:searchString];
return YES;
}
(on the second line)
- (void)handleSearchForTerm:(NSString *)searchTerm {
[self setSavedSearchTerm:searchTerm];
if ([self searchResults] == nil) {
NSMutableArray *array = [[NSMutableArray alloc] init];
[self setSearchResults:array];
[array release];
}
//Empty the searchResults array
[[self searchResults] removeAllObjects];
//Check if the searchTerm doesn't equal zero...
if ([[self savedSearchTerm] length] != 0) {
//Search the whole tableList (datasource)
for (NSString *currentString in tableList) {
NSString *klasString = [[NSString alloc] init];
NSInteger i = [[leerlingNaarKlasList objectAtIndex:[tableList indexOfObject:currentString]] integerValue];
if(i != -1) {
klasString = [klassenList objectAtIndex:(i - 1)];
}
//Check if the string matched or the klas (group of school)
if ([currentString rangeOfString:searchTerm options:NSCaseInsensitiveSearch].location != NSNotFound ||
[klasString rangeOfString:searchTerm options:NSCaseInsensitiveSearch].location != NSNotFound) {
//Add to results
[[self searchResults] addObject:currentString];
//Save the klas (group of school). It has the same index as the result (lastname)
NSString *strI = [[NSString alloc] initWithFormat:#"%i", i];
[[self searchResultsLeerlingNaarKlas] addObject:strI];
[strI release];
}
[klasString release];
}
}
}
Can someone help me out?
Regards,
Dodo
The double free error means you have released an object more than needed. Here the suspicious object is klasString.
From your code:
NSString *klasString = [[NSString alloc] init];
...
if(i != -1) {
klasString = [klassenList objectAtIndex:(i - 1)];
}
...
[klasString release];
The assignment inside the if statement
loses reference to the newly allocated NSString, introducing a memory leak
makes the later release apply to the object from klassenList. When klassenList releases its elements, a double free error will occur.

Sample Cocoa app, ref counting, [pool drain] yields EXC_BAD_ACCESS?

I'm working through Cocoa Programming for Mac OS X (3rd ed) and in chapter 4 I wrote this app:
int main (int argc, const char * argv[]) {
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
//create the date object
NSCalendarDate *now = [[NSCalendarDate alloc] init];
//seed random # generator
srandom(time(NULL));
NSMutableArray *array;
array = [[NSMutableArray alloc] init];
int i;
for (i=0; i<10; i++){
//create a date/time that is 'i' weeks from now
NSCalendarDate *iWeeksFromNow;
iWeeksFromNow = [now dateByAddingYears:0
months:0
days:(i*7)
hours:0
minutes:0
seconds:0];
//create a new instance of lottery entry
LotteryEntry *entry = [[LotteryEntry alloc] init];
[entry setEntryDate:iWeeksFromNow];
[array addObject:entry];
[entry release];
}
[now release];
now = nil;
for (LotteryEntry *entryToPrint in array) {
NSLog(#"%#", entryToPrint);
}
[array release];
array = nil;
NSLog(#"about to drain the pool... (%#)", pool);
[pool drain];
NSLog(#"done");
NSLog(#"GC = %#", [NSGarbageCollector defaultCollector]);
return 0;
}
The LotteryEntry class looks like this:
#implementation LotteryEntry
- (void)setEntryDate:(NSCalendarDate *)date
{
entryDate = date;
}
- (NSCalendarDate *)entryDate
{
return entryDate;
}
- (int)firstNumber
{
return firstNumber;
}
- (int)secondNumber
{
return secondNumber;
}
- (id)init
{
return [self initWithDate:[NSCalendarDate calendarDate]];
}
- (id)initWithDate:(NSCalendarDate *)date
{
if(![super init])
return nil;
NSAssert(date != nil, #"Argument must be non-nil");
firstNumber = random() % 100 + 1;
secondNumber = random() % 100 + 1;
entryDate = [date retain];
return self;
}
- (NSString *)description
{
NSString *result;
result = [[NSString alloc] initWithFormat:#"%# = %d and %d",
[entryDate descriptionWithCalendarFormat:#"%b %d %Y"],
firstNumber,
secondNumber];
return result;
}
- (void)dealloc
{
NSLog(#"deallocating %#", self);
[entryDate release];
[super dealloc];
}
#end
As you can see I'm retaining and releasing the objects here. I'm pretty sure my code matches the book's, however when I run the app, at the [pool drain] I get this message:
Program received signal:
“EXC_BAD_ACCESS”.
I'm not sure what's causing this. I'm hoping it's something stupid that I missed, but I'd sure appreciate a few other pairs of eyes on it. Thanks in advance!
(side note: I'm a .NET developer, so ref counting is pretty foreign to me!)
It also looks like you have a bug in this method:
- (id)initWithDate:(NSCalendarDate *)date
{
if(![super init])
return nil;
NSAssert(date != nil, #"Argument must be non-nil");
firstNumber = random() % 100 + 1;
secondNumber = random() % 100 + 1;
entryDate = [date retain];
return self;
}
You are essentially discarding the results from [super init], while it may not be a problem in this instance, it could cause serious problems in others. You should 'always' structure you init methods like this:
- (id)initWithDate:(NSCalendarDate *)date
{
if(self = [super init]) {
NSAssert(date != nil, #"Argument must be non-nil");
firstNumber = random() % 100 + 1;
secondNumber = random() % 100 + 1;
entryDate = [date retain];
}
return self;
If you are not going to return self from an init method (for instance it is a factory or something odd like that), you should remember to release self. It has been alloc'ed, and if you don't return it, it cannot be released properly. Example:
- (id) init
{
NSObject* newSelf = [[NSObject alloc] init];
[self release];
return newSelf;
}
doh! Just typing the code made me realize my problem. Doncha love it when that happens?
I am retaining the date in my init, but I still had that extra setEntryDate method that was not calling retain. Removing this and calling the initWithDate method instead seemed to fix the problem.
It's not related to your issue at hand but you should avoid using NSCalendarDate, it's been deprecated for a while now and will probably be removed from the API entirely soon.