SQLite + memory management issues - iphone

I have a SQLite database containing image data as a BLOB, and I am using the follow code to set the properties of an object, but I am getting major memory issues with the data, particularly on the data and w.wineImage objects, even though it looks like I am releasing everything correctly...
if(sqlite3_prepare_v2(database, sql, -1, &selectstmt, NULL) == SQLITE_OK) {
while(sqlite3_step(selectstmt) == SQLITE_ROW) {
Wine *w = [[Wine alloc] init];
w.wineId = sqlite3_column_int(selectstmt, 0);
w.wineName = [NSString stringWithUTF8String:(char *)sqlite3_column_text(selectstmt, 1)];
NSData *data = [[NSData alloc] initWithBytes:sqlite3_column_blob(selectstmt, 2) length:sqlite3_column_bytes(selectstmt, 2)];
if([data length] < 10){
UIImage *noImage = [UIImage imageNamed:#"no_image.png"];
w.wineImage = noImage;
[noImage release];
[data release];
} else {
UIImage *wineBottle = [[UIImage alloc] initWithData:data];
w.wineImage = wineBottle;
[wineBottle release];
[data release];
}
w.price = [NSString stringWithUTF8String:(char *)sqlite3_column_text(selectstmt, 3)];
[wineArray addObject:w];
//w=nil;
[w release];
}
sqlite3_close(database);

You're releasing noImage but you created it from imageNamed which is already autoreleased.
I don't know if that helps your problem but you definitely shouldn't be doing it :)

Related

SQLite and OBJ-C DB clear when launch application

I've a SQLite db on this position
/Users/software/Library/Application Support/iPhone Simulator/5.0/Applications/723EEE91-CB9D-4A27-8492-D61E127E72B7/myAPP.app/localDB.sqlite
No problems on write, delete or read data.
For example if I write on DB and close application, I find data in my DB, but if I restart the app my DB will clear. Can someone tell me why?
This is an example of a DB instance
SQLiteUtilities *db = [[SQLiteUtilities alloc]init:[[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:#"localDB.sqlite"]];
[db loadValuesFromQuery:#"SELECT * FROM categories;"];
for (int i = 0; i < [db getSize]; i++) {
NSLog(#"-- %#", [db objectAtIndex:i]);
}
[db release];
-(void)loadValuesFromQuery:(NSString *)query {
GenericRecord *record = [[[GenericRecord alloc]init] autorelease];
if (sqlite3_open([pathDB UTF8String], &database) == SQLITE_OK) {
// query che ricava i valori
const char *sql = (const char *) [query UTF8String];
sqlite3_stmt *selectstmt;
if(sqlite3_prepare_v2(database, sql, -1, &selectstmt, NULL) == SQLITE_OK) {
while(sqlite3_step(selectstmt) == SQLITE_ROW) {
// ricaviamo i valori letti dalla query
record.uniqueID = [NSString stringWithUTF8String:(char *)sqlite3_column_text(selectstmt, 1)];
record.superID = [NSString stringWithUTF8String:(char *)sqlite3_column_text(selectstmt, 2)];
record.subSuperID = [NSString stringWithUTF8String:(char *)sqlite3_column_text(selectstmt, 3)];
record.title = [NSString stringWithUTF8String:(char *)sqlite3_column_text(selectstmt, 4)];
record.description = [NSString stringWithUTF8String:(char *)sqlite3_column_text(selectstmt, 5)];
record.price = [NSString stringWithUTF8String:(char *)sqlite3_column_text(selectstmt, 6)];
record.note = [NSString stringWithUTF8String:(char *)sqlite3_column_text(selectstmt, 7)];
record.pictureURL = [NSString stringWithUTF8String:(char *)sqlite3_column_text(selectstmt, 8)];
record.catName = [NSString stringWithUTF8String:(char *)sqlite3_column_text(selectstmt, 9)];
record.subCatName = [NSString stringWithUTF8String:(char *)sqlite3_column_text(selectstmt, 10)];
NSData *data = [[NSData alloc] initWithBytes:sqlite3_column_blob(selectstmt, 11) length:sqlite3_column_bytes(selectstmt, 11)];
if(data) record.imageBig = [UIImage imageWithData:data];
[self.list addObject:record];
}
}
}
sqlite3_close(database);
}
Also curious why I'm seeing so much raw sqlite3 work these days - any reason you're not using Core Data? Just a suggestion - it may not fit your needs (i.e., schema already exists and you're downloading it directly, etc).

Potential Leak of an object

I am facing Potential leak of an object allocated. So how can I release my custom class object in loop . I am enclosing my code below herewith.
- (ProfileClass *) getUserProfile
{
NSString *query = [NSString stringWithFormat:#"SELECT * FROM Profile"];
NSLog(#"query %#",query);
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *path = [documentsDirectory stringByAppendingPathComponent:#"MOFAdb.sqlite"];
ProfileClass *profile = nil;
// Open the database. The database was prepared outside the application.
if(sqlite3_open([path UTF8String], &database) == SQLITE_OK)
{
sqlite3_stmt *Statement1;
//int i=0;
if (sqlite3_prepare_v2(database, [query UTF8String], -1, &Statement1, NULL) == SQLITE_OK) {
//int returnValue = sqlite3_prepare_v2(database, sql, -1, &Statement1, NULL);
if (sqlite3_step(Statement1) == SQLITE_ROW) {
// The second parameter indicates the column index into the result set.
NSString *userName = [NSString stringWithUTF8String:(char *)sqlite3_column_text(Statement1, 0)];
NSString *userEmail = [NSString stringWithUTF8String:(char *)sqlite3_column_text(Statement1, 1)];
NSString *phoneNum = [NSString stringWithUTF8String:(char *)sqlite3_column_text(Statement1, 2)];
//int phone = sqlite3_column_int(Statement1, 2);
//NSLog(#"%d",phone);
//RecipeClass *rc = [[RecipeClass alloc] getRecipe:recipeName withRecipeIng:recipeIng withRecipeInst:recipeInstru withRecipeTips:recipeTips withRecipeDesc:recipeDesc];
if (profile)
[profile release];
profile = [[ProfileClass alloc] getProfileInfo:userName withEmail:userEmail withPhone:phoneNum];
//NSLog(#"%#",fact);
//NSLog(#"%d",i);
//i++;
}
}
//Release the select statement memory.
sqlite3_finalize(Statement1);
//}
}
else {
// Even though the open failed, call close to properly clean up resources.
sqlite3_close(database);
NSAssert1(0, #"Failed to open database with message '%s'.", sqlite3_errmsg(database));
// Additional error handling, as appropriate...
}
return profile;
}
If I autorelease my profile = [[[ProfileClass alloc] getProfileInfo:userName withEmail:userEmail withPhone:phoneNum] autorelease]; so my application crashes later. So I m release on if check but build and Analyze shows it as a warning.
You can also autorelease like that:
return [profile autorelease];
and retain the object of ProfileClass where you used it,
Ex- ProfileClass *objProfile=[[database getUserProfile] retain];
and release objProfile when you used it.
Your method: - (ProfileClass *) getUserProfile is not an instance method or a copy you should return an object that is autoreleased. But you should do it on the last line, since you have a if/else structure and if you only autorelease it on line profile = [[[ProfileClass alloc] getProfileInfo:userName withEmail:userEmail withPhone:phoneNum] autorelease]; it will not get autoreleased if it fails the if statement and goes to else. So just do this:
return [profile autorelease];
Why don't you do:
return [profile autorelease];
And there is no need for the
if (profile)
check. Just release unconditionally. If profile is nil, it won't have any negative effect.
FWIW: I don't quite understand what your getProfile:etc... method does. I assume it is an initializer and nothing more (like the many initXYZ: methods in Cocoa). If so, you should probably call it initWithUserName:email:phone: to go with the convention. Could you post the method?
using an array you can solve this issue before calling this method
NSMutableArray *ProfileArray=[[NSMutableArray alloc] initWithArray:[ClassObj getUserProfile]];
ProfileClass *profileObj=[[ProfileArray objectAtIndex:0] retain];
[ProfileArray release];
// now you can use profile object anywhere... I hope memory issue is also solved
- (NSMutableArray *) getUserProfile
{
NSMutableArray *array=[[NSMutableArray alloc] init];
NSString *query = [NSString stringWithFormat:#"SELECT * FROM Profile"];
NSLog(#"query %#",query);
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *path = [documentsDirectory stringByAppendingPathComponent:#"MOFAdb.sqlite"];
ProfileClass *profile = nil;
// Open the database. The database was prepared outside the application.
if(sqlite3_open([path UTF8String], &database) == SQLITE_OK)
{
sqlite3_stmt *Statement1;
//int i=0;
if (sqlite3_prepare_v2(database, [query UTF8String], -1, &Statement1, NULL) == SQLITE_OK) {
//int returnValue = sqlite3_prepare_v2(database, sql, -1, &Statement1, NULL);
if (sqlite3_step(Statement1) == SQLITE_ROW) {
// The second parameter indicates the column index into the result set.
NSString *userName = [NSString stringWithUTF8String:(char *)sqlite3_column_text(Statement1, 0)];
NSString *userEmail = [NSString stringWithUTF8String:(char *)sqlite3_column_text(Statement1, 1)];
NSString *phoneNum = [NSString stringWithUTF8String:(char *)sqlite3_column_text(Statement1, 2)];
//int phone = sqlite3_column_int(Statement1, 2);
//NSLog(#"%d",phone);
//RecipeClass *rc = [[RecipeClass alloc] getRecipe:recipeName withRecipeIng:recipeIng withRecipeInst:recipeInstru withRecipeTips:recipeTips withRecipeDesc:recipeDesc];
if (profile)
[profile release];
profile = [[ProfileClass alloc] getProfileInfo:userName withEmail:userEmail withPhone:phoneNum];
[array addObject:profile];
[profile release];
}
}
//Release the select statement memory.
sqlite3_finalize(Statement1);
//}
}
else {
// Even though the open failed, call close to properly clean up resources.
sqlite3_close(database);
NSAssert1(0, #"Failed to open database with message '%s'.", sqlite3_errmsg(database));
// Additional error handling, as appropriate...
}
return [array autorelease];
}
I hope it will be helpful to you
cheers

Instruments says there is a memory leak in this method, but I can't figure out where

According to instrument there is memory leak at these lines
cat.catName = (catName)?[NSString stringWithUTF8String:catName]:#"";
NSData *dataForCachedImage = [[NSData alloc] initWithBytes:sqlite3_column_blob(statement, 2) length: sqlite3_column_bytes(statement, 2)];
cat.catThumb = [UIImage imageWithData:dataForCachedImage];
[dataForCachedImage release];
in the following code.
I have also statically analyzed the code.. shows no issue in this file.
-(NSMutableArray *)getAllItems{
NSMutableArray *items = [[[NSMutableArray alloc] init] autorelease];
const char *sql = "SELECT * FROM category ORDER by sort";
sqlite3_stmt *statement;
int sqlresult = sqlite3_prepare(database, sql, -1, &statement, nil);
if (sqlresult == SQLITE_OK) {
while (sqlite3_step(statement) == SQLITE_ROW) {
Category *cat = [[Category alloc] init];
char *catName = (char *)sqlite3_column_text(statement, 1);
cat.catID = sqlite3_column_int(statement, 0);
cat.catName = (catName)?[NSString stringWithUTF8String:catName]:#"";
NSData *dataForCachedImage = [[NSData alloc] initWithBytes:sqlite3_column_blob(statement, 2) length: sqlite3_column_bytes(statement, 2)];
cat.catThumb = [UIImage imageWithData:dataForCachedImage];
[dataForCachedImage release];
cat.catLock = sqlite3_column_int(statement, 3);
cat.sort = sqlite3_column_int(statement, 4);
cat.total = [self totalSMS:cat.catID];
[items addObject:cat];
[cat release];
}
sqlite3_finalize(statement);
}
else
{
NSLog(#"problem with the database");
NSLog(#"%d",sqlresult);
}
return items;}
can someone point out?
Thanks
In the first sample of code you aren't releasing dataForCachedImage. You can either change it to
cat.catName = (catName)?[NSString stringWithUTF8String:catName]:#"";
NSData *dataForCachedImage = [[NSData alloc] initWithBytes:sqlite3_column_blob(statement, 2) length: sqlite3_column_bytes(statement, 2)];
cat.catThumb = [UIImage imageWithData:dataForCachedImage];
[dataForCachedImage release];
or
cat.catName = (catName)?[NSString stringWithUTF8String:catName]:#"";
NSData *dataForCachedImage = [[[NSData alloc] initWithBytes:sqlite3_column_blob(statement, 2) length: sqlite3_column_bytes(statement, 2)] autorelease];
cat.catThumb = [UIImage imageWithData:dataForCachedImage];
ok.. resolved it!... the problem was that the catThumb was not released in Category class. thats it!, no other modification required... anyways, thx everybody!

Problem while scrolling Table View

I have the following problem while i scroll the table view:
NSCFString objectAtIndex:]: unrecognized selector sent to instance
I create NSDictionary tableContents and when i scroll it becomes deallocated.
This is my code:
- (void)viewDidLoad {
lessonsInGroup1 = [NSMutableArray array];
lessonsInGroup2 = [NSMutableArray array];
lessonsInGroup1 = [self grabRowsInGroup:#"1"];
lessonsInGroup2 = [self grabRowsInGroup:#"2"];
NSDictionary *temp =[[NSDictionary alloc]initWithObjectsAndKeys:lessonsInGroup1,#"General Information",lessonsInGroup2,#"LaTeX Examples", nil];
//[[tableContents alloc] init];
self.tableContents =temp;
[temp release];
NSLog(#"table %#",self.tableContents);
NSLog(#"table with Keys %#",[self.tableContents allKeys]);
self.sortedKeys =[[self.tableContents allKeys] sortedArrayUsingSelector:#selector(compare:)];
NSLog(#"sorted %#",self.sortedKeys);
[lessonsInGroup1 release];
[lessonsInGroup2 release];
//[table reloadData];
// Uncomment the following line to display an Edit button in the navigation bar for this view controller.
// self.navigationItem.rightBarButtonItem = self.editButtonItem;
[super viewDidLoad];
}
- (NSMutableArray *) grabRowsInGroup:(NSString*)GroupID{
NSMutableArray *groupOfLessons;
groupOfLessons = [[NSMutableArray alloc] init];
char *sqlStatement;
int returnCode;
sqlite3_stmt *statement;
NSString *databaseName;
NSString *databasePath;
// Setup some globals
databaseName = #"TexDatabase.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;
// Open the database from the users filessytem
if(sqlite3_open([databasePath UTF8String], &database) != SQLITE_OK) {
fprintf(stderr, "Error in opening the database. Error: %s",
sqlite3_errmsg(database));
sqlite3_close(database);
return;
}
sqlStatement = sqlite3_mprintf(
"SELECT * FROM Lessons WHERE LessonGroup = '%s';", [GroupID UTF8String]);
returnCode =
sqlite3_prepare_v2(database,
sqlStatement, strlen(sqlStatement),
&statement, NULL);
if(returnCode != SQLITE_OK) {
fprintf(stderr, "Error in preparation of query. Error: %s",
sqlite3_errmsg(database));
sqlite3_close(database);
return;
}
returnCode = sqlite3_step(statement);
while(returnCode == SQLITE_ROW) {
NSString *aLessonID = [NSString stringWithUTF8String:(char *)sqlite3_column_text(statement, 0)];
NSString *aLessonGroup = [NSString stringWithUTF8String:(char *)sqlite3_column_text(statement, 1)];
NSString *aLessonTopic = [NSString stringWithUTF8String:(char *)sqlite3_column_text(statement, 2)];
NSString *aLessonText = [NSString stringWithUTF8String:(char *)sqlite3_column_text(statement, 3)];
NSString *aLessonCode = [NSString stringWithUTF8String:(char *)sqlite3_column_text(statement, 4)];
NSString *aLessonPicture = [NSString stringWithUTF8String:(char *)sqlite3_column_text(statement, 5)];
/*NSLog(aLessonID);
NSLog(aLessonGroup);
NSLog(aLessonTopic);
NSLog(aLessonText);
NSLog(aLessonCode);
NSLog(aLessonPicture);*/
// Create a new busCit object with the data from the database
Lesson *lesson = [[Lesson alloc] initWithLessonID:aLessonID LessonGroup:aLessonGroup LessonTopic:aLessonTopic LessonText:aLessonText LessonCode:aLessonCode LessonPicture:aLessonPicture];
[groupOfLessons addObject:lesson];
returnCode = sqlite3_step(statement);
}
sqlite3_finalize(statement);
sqlite3_free(sqlStatement);
return [groupOfLessons autorelease];
}
What does your #property for tableofContents look like?
Also, you are going to run into issues with
[lessonsInGroup1 release];
[lessonsInGroup2 release];
because you are autoreleasing those in the grabRowsInGroup:
So, you don't need to call release them.
Looks like you are calling objectAtIndex on NSString. It should rather be some array

Problem With Sqlite Data Retrieving

I have an SQLite database, and when I am trying to get the data from the database, I get the last inserted element repeatedly. How can I get all the elements with no repetition.
The code I've written:
- (NSMutableArray *) gettingData {
sqlDict = [[NSMutableDictionary alloc] init];
membersInfoArray =[[NSMutableArray alloc]init ];
[self checkAndCreateDatabase];
if (sqlite3_open([databasePath UTF8String], &database) == SQLITE_OK)
{
const char *sql = "select * from ProductList";
sqlite3_stmt *selectstmt;
if(sqlite3_prepare_v2(database, sql, -1, &selectstmt, NULL) == SQLITE_OK)
{
while(sqlite3_step(selectstmt) == SQLITE_ROW)
{
NSString *prdbcode = [NSString stringWithUTF8String:(char *)sqlite3_column_text(selectstmt, 0)];
[sqlDict setObject:prdbcode forKey:#"Barcode"];
[prdbcode release];
NSString *prdname = [NSString stringWithUTF8String:(char *)sqlite3_column_text(selectstmt, 1)];
[sqlDict setObject:prdname forKey:#"ProductName"];
[prdname release];
NSString *prdDesc = [NSString stringWithUTF8String:(char *)sqlite3_column_text(selectstmt, 2)];
[sqlDict setObject:prdDesc forKey:#"ProductDescription"];
[prdDesc release];
NSString *prdstatus = [NSString stringWithUTF8String:(char *)sqlite3_column_text(selectstmt, 3)];
[sqlDict setObject:prdstatus forKey:#"ProductStatus"];
[prdstatus release];
[membersInfoArray addObject:sqlDict];
[sqlDict release];
}
}
sqlite3_finalize(selectstmt);
}
sqlite3_close(database);
return membersInfoArray;
}
I am retrieving the data as follows:
NSMutableArray *sqlArray = [sqlViewController gettingData];
Thank you.
Just Declare your array globally instead of declare locally in your method. Your problem will resolved.