i have 3 memory leaks in my application and i don't find how to fix it. I'm kind of new to xcode and objective c. Here is the code i have:
if(sqlite3_prepare_v2( [[DatabaseController sharedDatabaseController] getDb], sqlQueryConverted, -1, &dbStatement, NULL)==SQLITE_OK){
//Run the query
while ( sqlite3_step(dbStatement) == SQLITE_ROW )
{
const char *name = (const char *)sqlite3_column_text(dbStatement, 0);
int courseId = sqlite3_column_int(dbStatement, 1);
const char *location = (const char *)sqlite3_column_text(dbStatement, 2);
const char *date = (const char *)sqlite3_column_text(dbStatement, 3);
//Convert the returnedElement char to string
nameConverted = [[NSString alloc] initWithUTF8String:name];
locationConverted = [[NSString alloc] initWithUTF8String:location];
dateConverted = [[NSString alloc] initWithUTF8String:date];
Course *course = [[[Course alloc]initWithName:nameConverted _id:courseId location:locationConverted courseDate:dateConverted] autorelease];
//Add the course to the to a temporary list to remove duplicated items
[tempResults addObject:course];
}
[nameConverted release];
[locationConverted release];
[dateConverted release];
}
I have tried to autorelease it too. This code is being used to filter a search and reload a search display table. If i put the release line in the while statement the application would crash if i type 2 letters. How could i fix this?
Thanks.
EDIT: I've been going back and forth with this problem with no luck. I've come to the conclusion that there's something wrong with Instruments because it's still showing memory leaks. Here's the code as it is today and as i believe should fix the problem:
NSString *nameConverted = [[NSString alloc] initWithUTF8String:name];
NSString *locationConverted = [[NSString alloc] initWithUTF8String:location];
NSString *dateConverted = [[NSString alloc] initWithUTF8String:date];
Course *course = [[[Course alloc]initWithName:nameConverted _id:courseId location:locationConverted courseDate:dateConverted] autorelease];
//Add the course to the to a temporary list to remove duplicated items
[tempResults addObject:course];
course = nil;
[course release];
[nameConverted release];
nameConverted = nil;
[locationConverted release];
locationConverted = nil;
[dateConverted release];
dateConverted = nil;
NSLog(#"course retain count %i",[course retainCount]);
NSLog(#"name coverted retain count %i",[nameConverted retainCount]);
NSLog(#"location coverted retain count %i",[locationConverted retainCount]);
NSLog(#"date coverted retain count %i",[dateConverted retainCount]);
The logs are telling me that the retainCount = 0; so i don't understand why there's a memory leak. Can you guys give me some advice?
Thanks again.
You're leaking at each loop. You are only releasing the 3 lasts NSString. Everytime you re-assign a new NSString to your 3 variables (nameConverted, locationConverted, dateConverted) you loose the reference to the NSString objects they are pointing too. That means memory leaking. You only release the 3 last ones of them, when you get out of your While loop.
You are creating a memory block each while loop, and then only releasing it once. So if your while loop runs over 10 times, each string has a retain count of 10 but only released once.
To fix, put your 3 releases inside your while loop like this:
if(sqlite3_prepare_v2( [[DatabaseController sharedDatabaseController] getDb], sqlQueryConverted, -1, &dbStatement, NULL)==SQLITE_OK){
//Run the query
while ( sqlite3_step(dbStatement) == SQLITE_ROW )
{
const char *name = (const char *)sqlite3_column_text(dbStatement, 0);
int courseId = sqlite3_column_int(dbStatement, 1);
const char *location = (const char *)sqlite3_column_text(dbStatement, 2);
const char *date = (const char *)sqlite3_column_text(dbStatement, 3);
//Convert the returnedElement char to string
nameConverted = [[NSString alloc] initWithUTF8String:name];
locationConverted = [[NSString alloc] initWithUTF8String:location];
dateConverted = [[NSString alloc] initWithUTF8String:date];
Course *course = [[[Course alloc]initWithName:nameConverted _id:courseId location:locationConverted courseDate:dateConverted] autorelease];
//Add the course to the to a temporary list to remove duplicated items
[tempResults addObject:course];
[nameConverted release];
[locationConverted release];
[dateConverted release];
}
}
Have you tried to use:
nameConverted = [[NSString initWithUTF8String:name] autorelease];
locationConverted = [[NSString initWithUTF8String:location] autorelease];
dateConverted = [[NSString initWithUTF8String:date] autorelease];
And removing the releases outside the loop?
Also do not autoremove the course object, release it after adding it to tempResults.
This is how you should be doing it.
NSString *nameConverted = [[NSString alloc] initWithUTF8String:name];
NSString *locationConverted = [[NSString alloc] initWithUTF8String:location];
NSString *dateConverted = [[NSString alloc] initWithUTF8String:date];
Course *course = [[Course alloc] initWithName:nameConverted _id:courseId location:locationConverted courseDate:dateConverted];
[tempResults addObject:course];
[course release];
[dateConverted release];
[locationConverted release];
[nameConverted release];
Related
IM USING SQLITE DATABASE for my iPhone app
In my app in data base to retried the content from the database i used an array and i return this array in a database method for select statement,
for this purpose i allocate an array and i need to release the array,
NSMutableArray *allContacts = [[NSMutableArray alloc] init];
return allContacts;
if i release an array how sh'd i used in return statement
viceversa if i tried to release after return (we can do anything after return)
Any solution please....
How should we use auto release NSMutable array
//Select statement for contacts
//==================================
+ (NSMutableArray*) selectAllContactsFromDB
{
NSString *DBPath = [self copyDBFile];
sqlite3 *contactsDB = nil;
sqlite3_stmt *statement = nil;
NSMutableArray *allContacts = [[NSMutableArray alloc] init];
if (sqlite3_open([DBPath UTF8String], &contactsDB) == SQLITE_OK)
{
NSString *query = [NSString stringWithFormat: #"SELECT ROWID, NAME, MOBILE, FROM CONTACTINFO"];
if(sqlite3_prepare_v2(contactsDB, [query UTF8String], -1, &statement, NULL) == SQLITE_OK)
{
while (sqlite3_step(statement) == SQLITE_ROW)
{
Contact *contact = [[Contact alloc] init];
contact.rowid = sqlite3_column_int(statement, 0);
contact.name = [NSString stringWithUTF8String:(const char*) sqlite3_column_text(statement, 1)];
contact.mobile = [NSString stringWithUTF8String:(const char*) sqlite3_column_text(statement, 2)];
[allContacts addObject:contact];
}
}
else {
NSLog(#"Statement not prepared");
}
}
[DBPath release];
return allContacts;
}
When you return an allocated object from a method pass it as an autoreleased object.
return [allContacts autorelease];
When you get an autoreleased object you need to retain it for further use:
So change the calling method like;
NSMutableArray *temp = [[YourClass selectAllContactsFromDB] retain];
try like this
NSMutableArray *allContacts = [[[NSMutableArray alloc] init] autorelease];
and also like this..
return [allContacts autorelease];
You can write return statement
return [allContacts autorelease];
OR you can use ARC in your project.
You will have to use autorelease :
return [allContacts autorelease];
This way il will be released the next time the autorelease pool will be flushed. And you have followed the golden rule : For each alloc, copy or retain, you must have a release or autorelease.
use ARC(Automatic Reference Counting) or you have a property as a mutuable array and just return the array...
to get u started on ARC, watch this: http://www.youtube.com/watch?v=FxuI4e_Bj58
Hi, guys! i'm newbie in iphone development. I have problems with the memory leak. I have such code.
do {
int s = sqlite3_step(statement);
switch (s) {
case SQLITE_ROW:{
Article *a = [[[Article alloc] init] autorelease];
for (int i = 0; i < columnCount; i++) {
const char *columnName = sqlite3_column_name(statement, i);
if(strncmp(columnName, "title", strlen("title")) == 0){
const char* colStr = (char*)sqlite3_column_text(statement, i);
if(colStr != NULL)
a.title = [[[NSString alloc] initWithCString: colStr encoding:NSUTF8StringEncoding] autorelease];
continue;
}
if(strncmp(columnName, "author", strlen("author")) == 0){
const char* colStr = (char*)sqlite3_column_text(statement, i);
if(colStr != NULL)
a.author = [[[NSString alloc] initWithCString:colStr encoding:NSUTF8StringEncoding] autorelease];
continue;
}
if(strncmp(columnName, "description", strlen("description")) == 0){
const char* colStr = (char*)sqlite3_column_text(statement, i);
if(colStr != NULL)
a.description = [[[NSString alloc] initWithCString:colStr encoding:NSUTF8StringEncoding] autorelease];
continue;
}
if(strncmp(columnName, "link", strlen("link")) == 0){
const char* colStr = (char*)sqlite3_column_text(statement, i);
if (colStr)
a.link = [[[NSString alloc] initWithCString:colStr encoding:NSUTF8StringEncoding] autorelease];
continue;
}
if(strncmp(columnName, "imageUrl", strlen("imageUrl")) == 0){
const char* colStr = (char*)sqlite3_column_text(statement, i);
if (colStr)
a.imageUrl = [[[NSString alloc] initWithCString:colStr encoding:NSUTF8StringEncoding] autorelease];
continue;
}
if(strncmp(columnName, "pubDate", strlen("pubDate")) == 0){
const char* colStr = (char*)sqlite3_column_text(statement, i);
if(colStr)
a.pubDate = [[[NSString alloc] initWithCString:colStr encoding:NSUTF8StringEncoding] autorelease];
continue;
}
}
[array insertObject:a atIndex:0];
}
break;
case SQLITE_DONE:
sqlite3_finalize(statement);
dataForReadingAvailable = NO;
break;
default:{
NSLog(#"getArticlesForFeed:sqlite3_step failed.Error:%s",sqlErrMsg);
return nil;
}
break;
}
}while(dataForReadingAvailable);
Tools from Xcode shows that i have leak memory when i a'm allocated object and when i initialized it properties. But why it's happens. All objects is autorealeased, so i think that will not to be such situation.
Thanks.
I think perhaps you overwrite the allocated memory with the new pointer somewhere.
I would replace
Article *a = [[[Article alloc] init] autorelease];
with
Article *a = [[Article alloc] init];
//some code
[a release]; //when you don't need it anymore
This is not a good practice to use autorelease much. It can even slow down your app btw in case of many objects.
And the strings like
a.title = [[[NSString alloc] initWithCString: colStr encoding:NSUTF8StringEncoding] autorelease];
I would replace with
a.title = [NSString stringWithCString:colStr encoding:NSUTF8StringEncoding];
In this case you also rely on autorelease pool, but it's much simpler.
EDIT:
Indeed, your 'Article' allocation is in the do-while loop.
In string [array insertObject:a atIndex:0]; your aray retains object and you don't need it anymore, but you rely on autorelease. Analyzer (or what tool you're using) sees, that you placed allocation in the cycle's body without releasing it after each iteration. This way, on the second iteration of the loop, you overwrite your "a" object, loosing the old pointer and leaking memory this way (pool won't find this pointer, since "a" overwritten). Even if you pass your loop only once, the analyzer does not care and gives you a warning.
I have two problems
1) My App works fine on the device the first few times its run. Then it crashes after the First screen pops up(Tab BAr). If i connect the device to my MAC and then run the device app it works(not debug mode).
I Checked the crash Logs, it crashed cos of "EXC_BAD_ACCESS (SIGSEGV)" and Thread0 crashed.The error was NSAutorelease released a deallocated Object.
2) I ran the app using instruments on the simulator. It show a lot of leaks on this function call.
Here is a sample code. When i run it using Instruments it shows a leak on the "setObject" line.
//class A- subclass of NSObject
+(NSMutableDictionary *)Hello {
NSMutableDictionary *dctONE = [[NSMutableDictionary alloc]initWithCapacity:0];
NSMutableArray *arrKeys = [[NSMutableArray alloc] initWithCapacity:0];
[arrKeys addObject:#"Object1"];
[arrKeys addObject:#"Object2"];
[dctONE setObject:[NSString stringWithFormat:#"dsfsdf"] forKey:[arrKeys objectAtIndex:0]];
[dctONE setObject:[NSString stringWithFormat:#"dsfsdf"] forKey:[arrKeys objectAtIndex:1]];
[arrKeys release];
return dctONE;
}
/// class B
-(void)some_Function {
NSMutableDictionary * dct = [A Hello]; //all declarations are done
//do stuff with dct
[dct release];
}
WHy does it Leak at "setObject"?? I am releasing everything properly right? Only thing is the [NSString stringWithFormat:] but that is autorelease right??
This is driving me Crazy?
Are the two problems related??
PS: It Doesnt crash on the sim, and strangely doesnt crash even when i connect my device to my MAC and then test it on device(not debugging, directly clicking the app on the device)
EDIT:
-(NSMutableDictionary *) ExecuteDataSet:(NSString *)strQuery
{
NSMutableDictionary *dctResult = [[[NSMutableDictionary alloc] init] autorelease];
// BOOL isSucess = FALSE;
const char *sql = [strQuery UTF8String];
sqlite3_stmt *selectStatement;
//prepare the select statement
int returnValue = sqlite3_prepare_v2(database, sql, -1, &selectStatement, NULL);
if(returnValue == SQLITE_OK)
{
sqlite3_bind_text(selectStatement, 1, sql, -1, SQLITE_TRANSIENT);
//loop all the rows returned by the query.
NSMutableArray *arrColumns = [[NSMutableArray alloc] init];
for (int i=0; i<sqlite3_column_count(selectStatement); i++)
{
const char *st = sqlite3_column_name(selectStatement, i);
[arrColumns addObject:[NSString stringWithCString:st encoding:NSUTF8StringEncoding]];
}
int intRow =1;
while(sqlite3_step(selectStatement) == SQLITE_ROW)
{
NSMutableDictionary *dctRow = [[NSMutableDictionary alloc] init];
for (int i=0; i<sqlite3_column_count(selectStatement); i++)
{
int intValue = 0;
const char *strValue;
switch (sqlite3_column_type(selectStatement,i))
{
case SQLITE_INTEGER:
intValue = (int)sqlite3_column_int(selectStatement, i);
[dctRow setObject:[NSString stringWithFormat:#"%d",intValue] forKey:[arrColumns objectAtIndex:i]];
break;
case SQLITE_TEXT:
strValue = (const char *)sqlite3_column_text(selectStatement, i);
[dctRow setObject:[NSString stringWithCString:strValue encoding:NSUTF8StringEncoding] forKey:[arrColumns objectAtIndex:i]];
break;
default:
strValue = (const char *)sqlite3_column_value(selectStatement, i);
[dctRow setObject:[NSString stringWithCString:strValue encoding:NSUTF8StringEncoding] forKey:[arrColumns objectAtIndex:i]];
break;
}
}
[dctResult setObject:[dctRow retain] forKey:[NSString stringWithFormat:#"Table%d",intRow]];
intRow ++;
[dctRow release];
}
[arrColumns release];
}
return dctResult;
}
The leak appears to be in this line:
[dctResult setObject:[dctRow retain] forKey:[NSString stringWithFormat:#"Table%d",intRow]];
setObject will call retain on the object it stores, but you are actually retaining it manually, so its retain count will be 2 instead of 1, and when dctResult gets released, it won't be removed from memory.
You're doing a whole bunch of unnecessary work in +Hello. My first step at debugging is always to remove unnecessary complexity. Try it like this:
+(NSMutableDictionary *)Hello {
NSMutableDictionary *dctONE = [NSMutableDictionary dictionaryWithObjectsAndKeys:
#"dsfsdf", #"Object1", #"dsfsdf", #"Object2", nil];
return dctONE;
}
That NSMutableDictionary dictionaryWithObjectsAndKeys method takes a nil-terminated array that goes object, key, object, key. It returns an autoreleased NSMutableDictionary object, which is what you want to be returning.
There are no guarantees in this world, but I can darn near promise you that method won't leak.
NSString *strSql = #"select tblrecentsearch_id,xmlrequest,company,postcode,city,kilometer,date from tblrecentsearch";
returnValue = sqlite3_prepare_v2(database, [strSql UTF8String], -1, &selectStatement, NULL);
if(returnValue == SQLITE_OK)
{
arrRecentSearch=[[NSMutableArray alloc] init];
while(sqlite3_step(selectStatement)==SQLITE_ROW)
{
Search *ObjSearch = [[Search alloc]init];
ObjSearch.intRecentSearchId = sqlite3_column_int(selectStatement, 0);
ObjSearch.xmlRequest = [NSString stringWithCString:(char *)sqlite3_column_text_check(selectStatement, 1) encoding:NSUTF8StringEncoding];
ObjSearch.strCompnay=[NSString stringWithCString:(char *)sqlite3_column_text_check(selectStatement, 2) encoding:NSUTF8StringEncoding];
ObjSearch.strPostCode=[NSString stringWithCString:(char *)sqlite3_column_text_check(selectStatement, 3) encoding:NSUTF8StringEncoding];
ObjSearch.strPlace = [NSString stringWithCString:(char *)sqlite3_column_text_check(selectStatement, 4) encoding:NSUTF8StringEncoding];
ObjSearch.strKilometer = [NSString stringWithCString:(char *)sqlite3_column_text_check(selectStatement, 5) encoding:NSUTF8StringEncoding];
ObjSearch.strDate = [NSString stringWithCString:(char *)sqlite3_column_text_check(selectStatement, 6) encoding:NSUTF8StringEncoding];
[arrRecentSearch addObject:ObjSearch];
[ObjSearch release];
}
}
sqlite3_finalize(selectStatement);
I want release arrRecentSearch but it will return from function . How can i realese this array. Please help me.I am fetching data from databse.
just autorelease it :
return [arrRecentSearch autorelease];
Have a look at the apple memopry management guidelines for more information on how this works
If you are going to return an autoreleased object, you must remember to retain it if you wnat to keep it around later. i.e. if we have a function that returns an autoreleased array
- (NSArray *) getSearchResults {
return [[[NSArray alloc] init] autorelease];
}
and you want to remember the search results for later you must remember to do this :
...
NSArray *results = [[self getSearchResults] retain]; //!< Remember the retain here!
...
or, you might use a property to store it :
#property (nonatomic, copy) NSArray *searchResults;
...
self.searchResults = [self getSearchResults]; //!< The property handles the retain for you here
...
Either way, if you just leave it as autoreleased, it's going to vanish and you're going to get an exception!
EDIT: Just realised MustISignUp has answered this in a comment!
FILE *file = fopen([gpsFilePath UTF8String], "r");
char c[1024];
while(fgets(c, 1024, file)!=NULL)
{
NSString *cString = [[NSString alloc] initWithCString:c
encoding:NSMacOSRomanStringEncoding];
NSArray *split = [cString componentsSeparatedByString:#","];
if ([split count] != 3)
{
continue; //this should only happen on the first line
}
gpx = [gpx stringByAppendingString:[NSString stringWithFormat:#" <trkpt lat=\"%#\" lon=\"%#\"></trkpt>\n\n", [split objectAtIndex:0], [split objectAtIndex:1]]];
}
As others have pointed out, you are creating a lot of temporary objects. An awful lot. On top of that, the size of the temporary objects, at least gpx ones, is increasing with each pass of the loop. You might want to try something like:
NSMutableString *gpx = [NSMutableString string];
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
for(NSString *line in [[NSString stringWithContentsOfFile:gpsFilePath usedEncoding:NULL error:NULL] componentsSeparatedByString:#"\n"]) {
NSArray *split = [line componentsSeparatedByString:#","];
[gpx appendFormat:#" <trkpt lat=\"%#\" lon=\"%#\"></trkpt>\n\n", [split objectAtIndex:0], [split objectAtIndex:1]];
}
[pool release];
pool = NULL;
This example loads the contents of what's at gpsFilePath and splits it by new lines. Then, for each line, it splits the line on commas, and appends the results to the mutable string gpx. It wraps the processing part that creates lots of temporary objects in an autorelease pool so they get discarded as soon as possible.
At the end, the variable gpx will contain the processed results.
You're allocating several objects for each line of the file, and they aren't getting released because they're getting added to an autorelease pool, and the autorelease pool isn't getting a chance to drain. Add an autorelease pool that drains every some number of iterations:
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
FILE *file = fopen([gpsFilePath UTF8String], "r");
char c[1024];
int line = 1;
while(fgets(c, 1024, file)!=NULL) {
NSString *cString = [[NSString alloc] initWithCString:c encoding:NSMacOSRomanStringEncoding];
NSArray *split = [cString componentsSeparatedByString:#","];
if ([split count] != 3) { continue; } //this should only happen on the first line
gpx = [gpx stringByAppendingString:[NSString stringWithFormat:#" <trkpt lat=\"%#\" lon=\"%#\"></trkpt>\n\n",
[split objectAtIndex:0],[split objectAtIndex:1]]];
if(line % 1000 == 0) // drain the pool every 1000 iterations
{
[pool release];
pool = [[NSAutoreleasePool alloc] init];
}
line++;
}
[pool release];
You are allocating cString without releasing or autoreleasing it. You should do a [cString release] each time when you're done with it.
Also, like the others said, you should use your own autorelease pool, and append to the existing gpx instead of creating a new string each time.
Can you use chunks bigger than 1024?