How to remove NSString Related Memory Leaks? - objective-c

in my application this method shows memory leak how do i remove leak?
-(void)getOneQuestion:(int)flashcardId categoryID:(int)categoryId
{
flashCardText = [[NSString alloc] init];
flashCardAnswer=[[NSString alloc] init];
//NSLog(#"%s %d %s", __FILE__, __LINE__, __PRETTY_FUNCTION__, __FUNCTION__);
sqlite3 *MyDatabase;
sqlite3_stmt *CompiledStatement=nil;
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask,YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *MyDatabasePath = [documentsDirectory stringByAppendingString:#"/flashCardDatabase.sqlite"];
if(sqlite3_open([MyDatabasePath UTF8String],&MyDatabase) == SQLITE_OK)
{
sqlite3_prepare_v2(MyDatabase, "select flashCardText,flashCardAnswer,flashCardTotalOption from flashcardquestionInfo where flashCardId=? and categoryId=?", -1, &CompiledStatement, NULL);
sqlite3_bind_int(CompiledStatement, 1, flashcardId);
sqlite3_bind_int(CompiledStatement, 2, categoryId);
while(sqlite3_step(CompiledStatement) == SQLITE_ROW)
{
self.flashCardText = [NSString stringWithUTF8String:(char *)sqlite3_column_text(CompiledStatement,0)];
self.flashCardAnswer= [NSString stringWithUTF8String:(char *)sqlite3_column_text(CompiledStatement,1)];
flashCardTotalOption=[[NSNumber numberWithInt:sqlite3_column_int(CompiledStatement,2)] intValue];
}
sqlite3_reset(CompiledStatement);
sqlite3_finalize(CompiledStatement);
sqlite3_close(MyDatabase);
}
}
this method also shows leaks.....what's wrong with this method?
-(void)getMultipleChoiceAnswer:(int)flashCardId
{
if(optionsList!=nil)
[optionsList removeAllObjects];
else
optionsList = [[NSMutableArray alloc] init];
sqlite3 *MyDatabase;
sqlite3_stmt *CompiledStatement=nil;
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask,YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *MyDatabasePath = [documentsDirectory stringByAppendingString:#"/flashCardDatabase.sqlite"];
if(sqlite3_open([MyDatabasePath UTF8String],&MyDatabase) == SQLITE_OK)
{
sqlite3_prepare_v2(MyDatabase,"select OptionText from flashCardMultipleAnswer where flashCardId=?", -1, &CompiledStatement, NULL);
sqlite3_bind_int(CompiledStatement, 1, flashCardId);
while(sqlite3_step(CompiledStatement) == SQLITE_ROW)
{
[optionsList addObject:[NSString stringWithUTF8String:(char *)sqlite3_column_text(CompiledStatement,0)]];
}
sqlite3_reset(CompiledStatement);
sqlite3_finalize(CompiledStatement);
sqlite3_close(MyDatabase);
}
}
alt text http://www.freeimagehosting.net/uploads/5b8120982c.png

You don't actually use the objects you initialise at the top of the function:
flashCardText = [[NSString alloc] init];
flashCardAnswer=[[NSString alloc] init];
as you replace those objects with others later on:
self.flashCardText = [NSString stringWithUTF8String:(char *)sqlite3_column_text(CompiledStatement,0)];
self.flashCardAnswer= [NSString stringWithUTF8String:(char *)sqlite3_column_text(CompiledStatement,1)];
So those would seem to be the objects that are leaking.

[[NSString alloc] init] is completely pointless. It will return exactly equivalent of #"", except wiht more work. Its unlikely to leak, since the system will almost certainly return you a fixed constant empty string, releasing the [NSString alloc] in the init routine. But it is pointless and useless and bad code.
Other than that, your code looks OK. The second method might be considered to "leak" optionsList, simply because it creates it and it is never released, but its only created once so it should be fine.
Try running your program and doing the leak detection, then breaking in the debugger and using
po 0x4b2720 (replace with the address of the leaked objects)
to see what string is actually leaking.
Remember that Leaks can give false positives, especially if anything is cached.

[[NSString alloc] init]; is a useless phrase. Removing the two first lines would get rid of the leak.
EDIT: Note, too, that the two strings you pull from the database will vanish as soon as the autoreleasepool is drained, unless they are retained.
Redit: Concerning the second method; I cannot see any obvious leaks. NSCFStrings are created a lot, and often stick around. That doesn't mean they actually are leaks. From what I can see, everything in that method is either autoreleased or persistent.

-1/2
#"" is equivalent to [[[NSString alloc] init] autorelease];
[[NSString alloc] init] will leak.
Remember the rule: init/retain should be balanced by autorelease/release.

Related

EXC_BAD_ACESS error

I get that error EXC_BAD_ACESS at the following line:
NSString *titleVarName = [[NSString alloc] initWithFormat:#"%#%#",#"occasionTitle",i];
Here is the for loop where the above code line is located:
for (i=0; i < count; ++i)
{
//Save the occasionS details to NSUserDefaults
NSString *titleVarName = [[NSString alloc] initWithFormat:#"%#%#",#"occasionTitle",i];
NSString *dateVarName = [[NSString alloc] initWithFormat:#"%#%#",#"occasionDate",i];
NSString *imageVarName = [[NSString alloc] initWithFormat:#"%#%#",#"occasionImage",i];
[[NSUserDefaults standardUserDefaults] setValue:[[[self displayedObjects] objectAtIndex:i]
title] forKey:titleVarName];
[[NSUserDefaults standardUserDefaults] setValue:[[[self displayedObjects] objectAtIndex:i]
date] forKey:dateVarName];
[[NSUserDefaults standardUserDefaults] setValue:[[[self displayedObjects] objectAtIndex:i]
imagePath] forKey:imageVarName];
//release
[titleVarName release];
[dateVarName release];
[imageVarName release];
[self dismissModalViewControllerAnimated:YES];
}
Isn't ok to alloc objects and release them inside a for loop?
You need to use %d or %i specifier instead of %# to specify an integer. If %# is used with int then it will try to access the object at the address specified by the int. For example, if the value of i is one then it is trying to access the object at address one which will cause a bad access.
NSString *titleVarName = [[NSString alloc] initWithFormat:#"%#%d",#"occasionTitle",i];
And also you don't need alloc and release here, though that is not the reason of bad access. You can use a convenience constructor.
NSString *titleVarName = [NSString stringWithFormat:#"occasionTitle%d", i];
// release not required
Do the same for dateVarName and imageVarName too.
Assuming i is an int, that line should be
NSString *titleVarName = [[NSString alloc] initWithFormat:#"%#%i",#"occasionTitle",i];
%# is used for Cocoa objects, not primitives like an int, float or bool;
Use the %# format specifier only for NSObject objects.
As i is an integer in your code, you have to use %d or %i for integers.
Moreover, there is no need to include the string using %#, you can use the static string directly in your format string:
NSString *titleVarName = [[NSString alloc] initWithFormat:#"occasionTitle%i",i];

Append a NSString as the first line of another NSString

I have two NSString, A and B.
I would that A becomes B\nA.
How can I do?
If in a method I use
NSString *string_B = [[NSString alloc] initWithString:#"something_from_a_DB"];
NSString *string_A = [[NSString alloc] initWithString:#"something_from_a_DB"];
if (aTrueCondition) {
string_C = [NSString stringWithFormat:#"%#\n%#", string_B, string_A];
} else {
string_C = string_A;
}
is string_C = string_A; a memory leak or is it good?
I added [string_A release], as string_C is a retained property. Now it works.
This is the way to put them together:
NSString *newString = [NSString stringWithFormat:#"%#\n%#", stringB, stringA];
The second part is “A becoming newString”. This is hard to do, as regular strings are immutable in Cocoa. The best thing you can do is throw out the old A and point A to the new string:
NSString *strA = #"foo";
NSString *strB = #"bar";
strA = [NSString stringWith…];
Just be careful not to leak A:
NSString *strA = [[NSString alloc] initWithString:#"foo"];
strA = [NSString stringWith…]; // this is a leak
NSString *str=[NSString stringWithFormat:#"%#\n%#",B,A];
use this.
NSString *stringA = [NSString stringWithFormat:#"%#\n%#", stringB, stringA];

about get value from sqlite

Code Sample:
NSString *str= [NSString stringWithUTF8String:(char *)sqlite3_column_text(selectStatement, 1)];
Test *t=[[Test alloc] init];
t.str=[str copy]; // why use "copy" here?
[str release];
It looks to be bad coding, for two reasons. Since str is being discarded, you might as well assign it, rather than a copy, to t.str. Second, and more importantly, setters should take care of retaining or copying data.
Should be:
NSString *str= [NSString stringWithUTF8String:(char *)sqlite3_column_text(selectStatement, 1)];
Test *t=[[Test alloc] init];
t.str=str; // No point copying to release original.

Alter NSString to get nothing back

At two stages of my app's runtime, I am sending an NSString to the following method.
I receive no warnings or errors in the code editor or the debugger, but when I NSLog the output (secondString), I only get given the memory address of the object.
Is there something I am not doing right here?
- (NSString*)validateString
{
NSString *firstString = [NSString stringWithFormat:#"%#", self];
[firstString stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
NSMutableString *secondString = [firstString mutableCopy];
[secondString replaceOccurrencesOfString:#"&" withString:#"%26" options:NSCaseInsensitiveSearch range:NSMakeRange([secondString length], 0)];
secondString = [NSString stringWithFormat:#"%#", secondString];
NSLog (#"%# and %#", firstString, secondString);
return secondString;
[firstString release];
[secondString release];
}
I'd appreciate any help.
Thanks,
Ricky.
Ugh that code is wrong on so many levels.
Here's a simpler version:
- (NSString*)validateString {
NSString *firstString = [self stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
NSString *secondString = [firstString stringByReplacingOccurrencesOfString:#"&" withString:#"%%26" options:NSCaseInsensitiveSearch range:NSMakeRange(0, [firstString length])];
NSLog (#"%# and %#", firstString, secondString);
return secondString;
}
The construct [NSString stringWithFormat:#"%#", aString] is a pretty useless statement, especially when you have copy available. You also have a memory leak and a crash in your code (you create a copy of a string [+1 retain count], assign an autoreleased string into the same variable [+0 retain count, original string lost and leaked], and then release the autoreleased string [crash when the autorelease pool drains]).
First some comments:
1) Everything after the return will not be executed, so the last to statements are useless (dead code).
2) If not created with +alloc, you can assumed that NSString instances are autoreleased, thus you do not need to send the -release message to firstString.
Edit: As Peter Hosey pointed out, you must however release the string obtained by -mutableCopy.
To answer your question:
-stringByAddingPercentEscapesUsingEncoding: returns a pointer to the newly created instance, so you have to save it.
- (NSString*)validateString
{
NSString *firstString = [NSString stringWithFormat:#"%#", self];
firstString = [firstString stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
NSMutableString *secondString = [firstString mutableCopy];
[secondString replaceOccurrencesOfString:#"&" withString:#"%26" options:NSCaseInsensitiveSearch range:NSMakeRange([secondString length], 0)];
secondString = [NSString stringWithFormat:#"%#", secondString];
NSLog (#"%# and %#", firstString, secondString);
[secondString release];
return secondString;
}
You need to use %%26 if you want the string "%26"
your NSMakeRange is backwards
your return is too early, and you don't need to release the strings anyway

Why am I getting an invalid NSString from SQLite for my UILabel?

I have a view with a UILabel and a UITableView. I'm using this code to get a string from a database:
-(void)getOneQuestion:(int)flashcardId categoryID:(int)categoryId {
flashCardText=[[NSString alloc] init];
flashCardAnswer=[[NSString alloc] init];
NSString *martialStr=[NSString stringWithFormat:#"%d", flashcardId];
NSString *queryStr=[[NSString alloc] initWithString:#"select flashCardText,flashCardAnswer,flashCardTotalOption from flashcardquestionInfo where flashCardId="];
queryStr=[queryStr stringByAppendingString:martialStr];
NSString *martialStr1=[NSString stringWithFormat:#"%d", categoryId];
NSString *queryStr2=[[NSString alloc] initWithString:#" and categoryId="];
queryStr2=[queryStr2 stringByAppendingString:martialStr1];
queryStr=[queryStr stringByAppendingString:queryStr2];
unsigned int lengthOfString=[queryStr length];
char temp2[lengthOfString +1];
strcpy(temp2, [queryStr cStringUsingEncoding:NSUTF8StringEncoding]);
clsDatabase *clsDatabaseObject = [[clsDatabase alloc] init];
sqlite3_stmt *dataRows = [clsDatabaseObject getDataset:temp2];
while(sqlite3_step(dataRows) == SQLITE_ROW) {
flashCardText =[NSString stringWithUTF8String:(char *)sqlite3_column_text(dataRows,0)];
flashCardAnswer=[NSString stringWithUTF8String:(char *)sqlite3_column_text(dataRows,1)];
flashCardTotalOption=sqlite3_column_int(dataRows,2);
}
sqlite3_reset(dataRows);
sqlite3_finalize(dataRows);
[clsDatabaseObject release];
}
When I click on the table cell, the string value (flashCardAnswer) shows invalid.
Although this code snippet doesn't seem to show where the string value is assigned to the UI element, it would seem that the problem could stem from your use of +[NSString stringWithUTF8String:] inside the while loop. This returns an autoreleased string which you much retain if you want to use it outside the scope of the method. Since those appear to be instance variables that you use in another part of the code to change the UI, you have a few options:
Send a -retain to each of them before exiting the method.
Use +alloc and -initWithUTF8String:.
Use a setter method or property that takes care of the details for you. (Thanks, Chuck!)
As a bonus, I have a few other related suggestions.
You're leaking memory by allocating strings for flashCardText and flashCardAnswer at the start of the method, since you overwrite them in the while loop.
Use -[NSString getCString:maxLength:encoding:] to write the query string into a char* buffer without the strcpy() call, or just use the char* from -cStringUsingEncoding: directly.
There is a lot of potential for simplifying the construction of your query string — definitely investigate NSMutableString. For example...
NSMutableString* query = [[NSMutableString alloc] initWithString:#"select flashCardText,flashCardAnswer,flashCardTotalOption from flashcardquestionInfo"];
[query appendFormat:#" where flashCardId=%d", flashcardId];
[query appendFormat:#" and categoryId=%d", categoryId];
clsDatabase *clsDatabaseObject = [[clsDatabase alloc] init];
sqlite3_stmt *dataRows = [clsDatabaseObject getDataset:[query cStringUsingEncoding:NSUTF8StringEncoding]];