sqlite3 - stringWithUTF8String is leaking! - objective-c

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

Related

Objective-C Bad exception dropbox

I've got a question. Where is the reason for EXC_BAD_ACCESS in the following code ?
-(void)restClient:(DBRestClient *)client loadedMetadata:(DBMetadata *)metadata {
if(metadata.isDirectory) {
db_Path = metadata.path;
int i = 0;
NSString *fileName = [[NSString alloc] init];
for(DBMetadata *file in metadata.contents) {
fileName = [NSString stringWithFormat:#"%#", file.filename];
[db_MetaFileNames addObject:file.filename];
i++;
}
[self createMetaListArray];
[fileName release];
}
}
-(void)createMetaListArray {
fileNamesAtDirectory = db_MetaFileNames;
for (int i=0; i < [fileNamesAtDirectory count]; i++) {
NSString *filePathWithName = db_directory;
[filePathWithName stringByAppendingFormat:
[fileNamesAtDirectory objectAtIndex:i]];
[filePathsAtDirectory addObject:filePathWithName];
[filePathWithName release];
}
}
Can Anyone here help me ?
Here:
NSString *fileName = [[NSString alloc] init];
for(DBMetadata *file in metadata.contents) {
fileName = [NSString stringWithFormat:#"%#", file.filename];
The NSString on the first line gets overwritten with the new values on the third line. The original value leaks.
This means that:
}
[self createMetaListArray];
[fileName release];
The release on the last line releases not the fileName that you alloc/init above, but the assignment inside the loop. You don't alloc/copy/retain that, so you're not "in charge" of releasing it.
You have a similar misunderstanding in the second function.
[filePathWithName stringByAppendingFormat:[fileNamesAtDirectory objectAtIndex:i]];
This does not amend filePathWithName. It returns a new string.
I suggest you read up on Cocoa's memory management rules -- you're missing some fundamentals. Understanding those will make your life a lot easier.

NSArray with componentsSeparatedByString memory leak

I'm having problems with memory leaks with this function. I thought creating an NSArray with componentsSeparatedByString was autorelease but instruments seems to indicate a leak at the NSArray aPair. Why would it indicate a leak there and not also at the other NSArrays created in the same way?
-(void) checkRequest: (NSString *)request view:(UIViewController *)theView webView:(UIWebView *)wView
{
//NSLog(#"JSResponder - checkRequest()");
NSString *aRequest = [NSString stringWithString:request];
NSArray *urlArray = [aRequest componentsSeparatedByString:#"?"];
if([urlArray count] > 1)
{
NSString *paramsString = [urlArray lastObject];
NSString *cmd = #"";
NSArray *urlParamsArray = [paramsString componentsSeparatedByString:#"&"];
int numCommands = [urlParamsArray count];
NSMutableDictionary *paramsWithNames = [[NSMutableDictionary alloc ] initWithCapacity:numCommands];
for (NSString *elementPair in urlParamsArray)
{
NSArray *aPair = [elementPair componentsSeparatedByString:#"="];
NSString *aKey = [aPair objectAtIndex:0];
NSString *aParam = [aPair objectAtIndex:1];
if([aKey compare:#"_command"] == NSOrderedSame)
{
cmd = aParam;
}
else
{
[paramsWithNames setValue: aParam forKey:aKey];
}
}
[self executeCommand: cmd withParams: paramsWithNames view:theView webView:wView];
[paramsWithNames release];
}
}
This function get called by the following:
- (void)pullJSEvent:(NSTimer*)theTimer
{
NSLog(#"MainView - pullJSEvent()");
NSString *jsCall = [NSString stringWithString:#"if(typeof checkOBJCEvents == 'function'){checkOBJCEvents();}"];
NSString *jsAnswer = [[webView stringByEvaluatingJavaScriptFromString:jsCall] retain];
if([jsAnswer compare:#"none"] != NSOrderedSame)
{
//NSLog(#" answer => %#", jsAnswer);
[jsResponder checkRequest:jsAnswer view:(UIViewController *)self webView:self.webView];
}
[jsAnswer release];
}
Thank-you
You're going to have to dig a bit deeper with the Leaks instrument. You're leaking one of the strings in the array, not the array itself. Leaks indicates that line because that's where the strings in the array are allocated.
Go into Leaks, look at a leaked instance, and click that little arrow button. You'll see all the retains and releases of the leaked object, which should point you to the problem.

release of previously deallocated object issue

I have a function which use for read one single line from a csv file.
But I got a release of previously deallocated object error, or sometimes the it is "double free" error.
I try to track down which object causes this error base on the error memory address, but I failed to do this.
Here's the code:
#interface CSVParser : NSObject {
NSString *fileName;
NSString *filePath;
NSString *tempFileName;
NSString *tempFilePath;
//ReadLine control
BOOL isFirstTimeLoadFile;
NSString *remainContent;
}
#property(nonatomic,retain) NSString *fileName;
#property(nonatomic,retain) NSString *filePath;
#property(nonatomic,retain) NSString *tempFileName;
#property(nonatomic,retain) NSString *tempFilePath;
#property(nonatomic,retain) NSString *remainContent;
-(id)initWithFileName:(NSString*)filename;
-(BOOL)checkAndCopyFile:(NSString *)filename;
-(BOOL)checkAndDeleteTempFile;
-(NSString*)readLine;
-(NSArray*)breakLine:(NSString*)line;
#end
#implementation CSVParser
#synthesize fileName;
#synthesize filePath;
#synthesize tempFileName;
#synthesize tempFilePath;
#synthesize remainContent;
-(id)initWithFileName:(NSString *)filename{
//ReadLine control
isFirstTimeLoadFile = TRUE;
self.fileName = filename;
self.tempFileName = [[NSString alloc] initWithFormat:#"temp_%#",fileName];
NSArray *documentPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentDir = [documentPaths objectAtIndex:0];
self.filePath = [documentDir stringByAppendingPathComponent:fileName];
self.tempFilePath = [documentDir stringByAppendingPathComponent:tempFileName];
if ([self checkAndCopyFile:fileName]) {
return self;
}else {
return #"Init Failure";
}
}
-(BOOL)checkAndCopyFile:(NSString *)filename{
BOOL isFileExist;
NSError *error = nil;
NSFileManager *fileManger = [NSFileManager defaultManager];
isFileExist = [fileManger fileExistsAtPath:filePath];
if (isFileExist) {
//Create a temp file for reading the line.
[fileManger copyItemAtPath:filePath toPath:tempFilePath error:&error];
return TRUE;
}else {
return FALSE;
}
}
-(NSString*)readLine{
NSError *error = nil;
//Read the csv file and save it as a string
NSString *tempFirstLine = [[[NSString alloc] init] autorelease];
NSString *stringFromFileAtPath = [[NSString alloc] init];
if (isFirstTimeLoadFile) {
NSLog(#"Into First Time");
stringFromFileAtPath = [NSString stringWithContentsOfFile:tempFilePath
encoding:NSUTF8StringEncoding
error:&error];
isFirstTimeLoadFile = FALSE;
}else {
NSLog(#"Not First Time");
NSLog(#"Not First Time count:%d",[remainContent retainCount]);
stringFromFileAtPath = remainContent;
remainContent = nil;
}
if ([stringFromFileAtPath isEqualToString:#""]) {
[stringFromFileAtPath release];
return #"EOF";
}
//Get the first line's range
NSRange firstLineRange = [stringFromFileAtPath rangeOfString:#"\n"];
//Create a new range for deletion. This range's lenght is bigger than the first line by 1.(Including the \n)
NSRange firstLineChangeLineIncludedRange;
if (stringFromFileAtPath.length > 0 && firstLineRange.length == 0) {
//This is the final line.
firstLineRange.length = stringFromFileAtPath.length;
firstLineRange.location = 0;
firstLineChangeLineIncludedRange = firstLineRange;
}else {
firstLineRange.length = firstLineRange.location;
firstLineRange.location = 0;
firstLineChangeLineIncludedRange.location = firstLineRange.location;
firstLineChangeLineIncludedRange.length = firstLineRange.length + 1;
}
//Get the first line's content
tempFirstLine = [stringFromFileAtPath substringWithRange:firstLineRange];
remainContent = [stringFromFileAtPath stringByReplacingCharactersInRange:firstLineChangeLineIncludedRange withString:#""];
[stringFromFileAtPath release];
error = nil;
return tempFirstLine;
}
And the following code shows how I use the class above:
CSVParser *csvParser = [[CSVParser alloc] initWithFileName:#"test.csv"];
BOOL isFinalLine = FALSE;
while (!isFinalLine) {
NSString *line = [[NSString alloc] init];
line = [csvParser readLine];
if ([line isEqualToString:#"EOF"]) {
isFinalLine = TRUE;
}
NSLog(#"%#",line);
[line release];
}
[csvParser release];
If I run the code, and finish the csv parsing, the App's main function will give me the double free error when it try to free the autorelease pool."* __NSAutoreleaseFreedObject(): release of previously deallocated object (0x6a26050) ignored"
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
int retVal = UIApplicationMain(argc, argv, nil, nil);
Could someone help me solve this issue?
Thank you!
[pool release];
Do not use -retainCount.
The absolute retain count of an object is meaningless.
You should call release exactly same number of times that you caused the object to be retained. No less (unless you like leaks) and, certainly, no more (unless you like crashes).
See the Memory Management Guidelines for full details.
There are a few problems in your code:
you aren't following the correct init pattern. You should have a self = [super init...]; if (self) {...} in there somewhere.
tempFileName is a retain property and you assign it the result of alloc/init. It will be leaked.
An immutable empty string ([[NSString alloc] init]) is pretty much never useful. And, in fact, stringFromFileAtPath is being leaked (technically -- implementation detail wise there is an empty immutable singleton string and thus, no real leak, but.... still...)
Finally, the crash: your readLine method correctly returns an autoreleased object. Yet, your while() loop consuming the return value of readLine is also releaseing that return value, leading to a double-release and an attempt to free that which was already freed.
You should "build and analyze" your code. I bet the llvm static analyzer would identify most, if not all, of the problems I mentioned above (and probably some more I missed).
When building with the analyzer, do you have either "all messages" or "analyzer issues only" selected in the Build window? Because, looking at the code, I'm surprised the analyzer didn't catch the obvious problem with stringFromFileAtPath.
Excerpting the code, you have the following lines that manipulate stringFromFileAtPath:
NSString *stringFromFileAtPath = [[NSString alloc] init];
....
stringFromFileAtPath = [NSString stringWithContentsOfFile:tempFilePath
encoding:NSUTF8StringEncoding
error:&error];
....
stringFromFileAtPath = remainContent;
....
[stringFromFileAtPath release];
And remainContent is set by:
remainContent = [stringFromFileAtPath stringByReplacingCharactersInRange:firstLineChangeLineIncludedRange
withString:#""];
You are releasing an autoreleased object. By memory keeps going up, how are you measuring it? Don't use Activity Monitor as it is nearly as useless to developers as retainCount is misleading. Use Instruments.
Your tempFirstLine NSString object is declared with autorelease, and is returned as your NSString line, which is then released.
Try using this:
while (!isFinalLine) {
NSString *line = [csvParser readLine];
if ([line isEqualToString:#"EOF"]) {
isFinalLine = TRUE;
}
NSLog(#"%#",line);
}
Replac this:
NSString *stringFromFileAtPath = [[NSString alloc] init];
with this:
NSString *stringFromFileAtPath = nil;
and get rid of the [stringFromFileAtPath release] statements.
The first line creates a pointer to a new string object that you never use, because you immediately overwrite the pointer with a pointer to string objects from elsewhere, which you don't need to release because you don't own them/didn't create them. Since you are releasing them, you're getting a crash.
You make the same mistake with tempFirstLine.

Problem in memory manegment?

I developing an application, in which i working with database manipulation. The method i have written in database class as follows.
-(NSMutableArray *)getData: (NSString *)dbPath{
NSMutableArray *dataArray = [[NSMutableArray alloc] init];
if(sqlite3_open([dbPath UTF8String], &database) == SQLITE_OK){
NSString *sqlQuery = [NSString stringWithFormat:#"SELECT empID, addText FROM Employee WHERE nameID = %#", nameID];
sqlite3_stmt *selectstmt;
if(sqlite3_prepare_v2(database, [sqlQuery UTF8String], -1, &selectstmt, NULL) == SQLITE_OK){
while (sqlite3_step(selectstmt) == SQLITE_ROW){
[dataArray addObject:[[NSMutableDictionary alloc] init]];
[[dataArray lastObject] setObject:[NSString
stringWithFormat:#"%d", sqlite3_column_int(selectstmt, 0)] forKey:#"empID"];
[[dataArray lastObject] setObject:[NSString stringWithUTF8String:(char *)sqlite3_column_text(selectstmt,1)] forKey:#"addText"];
}
}
sqlite3_finalize(selectstmt);
}
sqlite3_close(database);
return dataArray;
}
The above code work fine on the simulator but cannot on device.
I also was tracing the memory leaks , in which i founding that memory leak in above method code. But i not able to solve the that memory leak.
Now i also find out memory leak in following method.
(id)initWithString:(NSString *)str attributes:(NSDictionary *)attributes
{
if ((self = [super init]))
{
_buffer = [str mutableCopy];
_attributes = [NSMutableArray arrayWithObjects:[ZAttributeRun attributeRunWithIndex:0 attributes:attributes], nil];
}
return self;
}
The leak near _buffer = [str mutableCopy];. And leak trace gives me in the output continuous increasing NSCFString string allocation. How i maintain it?
Thanks in advance.
Your leak is that you don't release either the dataArray object, nor the mutable dictionaries you create in the while loop. Consider autoreleasing the mutable array, and manually releasing the dictionaries after you add them to the array.
As for why it "doesn't work" on the device, you need to be more specific about what happens and why that isn't what you expect.
Your inner loop leaks the NSMutableDictionary objects, as you should release them after adding to the array, i.e.
NSMutableDictionary *dict = [[NSMutableDictionary alloc] init];
[dict setObject:[NSString stringWithFormat:#"%d", sqlite3_column_int(selectstmt, 0)] forKey:#"empID"];
[dict setObject:[NSString stringWithUTF8String:(char *)sqlite3_column_text(selectstmt,1)] forKey:#"addText"];
[dataArray addObject:dict];
[dict release];
Also, your whole method should most probably return an autoreleased object by naming convention. Not sure if this is a leak - depends on how you call that method and if you release the returned value.
So maybe use
return [dataArray autorelease];
From the first glance you have 2 places where leaks can be:
NSMutableArray *dataArray = [[NSMutableArray alloc] init];
...
return dataArray;
Caller method is responsible for releasing array returned from your method - check if it does.
Also your method name is not consistent with obj-c guidelines - they suggest that methods returning non-autoreleased object( so caller is responsible for releasing them) should contain create, alloc, copy in their name. So it could be better to return autoreleased array (return [dataArray autorelease]; from this method and let caller decide whether it needs to retain array or not.
Second place is
[dataArray addObject:[[NSMutableDictionary alloc] init]];
It is leaking dictionary object, you should probably just write
[dataArray addObject:[NSMutableDictionary dictionary]];
Your method contains two call to +alloc that don't have corresponding calls to -release or -autorelease.
NSMutableArray *dataArray = [[NSMutableArray alloc] init];
...
[dataArray addObject:[[NSMutableDictionary alloc] init]];
You can rewrite these lines like this to get rid of the leak:
NSMutableArray *dataArray = [NSMutableArray array];
...
[dataArray addObject:[NSMutableDictionary dictionary]];

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

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:] ) ??