i have a problem for some time which i can't solve:
In the applicationDidFinishLaunching i have this code:
[self checkAndCreateDatabase];
[self readCharsFromDatabase];// which stores into an array some objects
[self readGlyphsFromDatabaseAtId:#"a"];// idem
The second array i'm using into a secondary ViewController and i'm getting the array in the viewDidLoad with:
AppDelegate *delegate = [[UIApplication sharedApplication] delegate];
self.array = delegate.array2;
All perfect till now, just that i want to run a new query before getting the array2. I'm trying with this:
AppDelegate *delegate = [[UIApplication sharedApplication] delegate];
[delegate readGlyphsFromDatabaseAtId:#"b"];// which is supposed to override my array2 with new values
self.array = delegate.array2;
This stops my application without error. I get this message only:
GNU gdb 6.3.50-20050815 (Apple version gdb-966) (Tue Mar 10 02:43:13 UTC 2009)
Copyright 2004 Free Software Foundation, Inc.
GDB is free software, covered by the GNU General Public License, and you are
welcome to change it and/or distribute copies of it under certain conditions.
Type "show copying" to see the conditions.
There is absolutely no warranty for GDB. Type "show warranty" for details.
This GDB was configured as "i386-apple-darwin".sharedlibrary apply-load-rules all
Attaching to process 4736.
My method is called till this point, after it i don't get a NSLog neither in the if, neither in the else:
if(sqlite3_open([databasePath UTF8String], &database) == SQLITE_OK)
Can someone help? Or if the logic is wrong to point me to the correct way of doing it?
If it's not to large for the memory i can store all the database in some arrays, it has 400kb
Thanks!
EDIT: This is how my function looks:
-(void) readGlyphsFromDatabaseAtId:(NSString *)charId {
NSLog(#"reading glyphs for id %#", charId);
// Setup the database object
sqlite3 *database;
// Init the animals Array
glyphs = [[NSMutableArray alloc] init];
NSLog(#"1");
// Open the database from the users filessytem
if(sqlite3_open([databasePath UTF8String], &database) == SQLITE_OK) {
NSLog(#"reading row");
// Setup the SQL Statement and compile it for faster access
NSString *query = [NSString stringWithFormat:#"SELECT nr,title,description FROM `glyphs` WHERE `id`='%#'", charId];
NSLog(#"%#", query);
//const char *sqlStatement = "SELECT nr,title,description FROM `glyphs` WHERE `id`='b'";
sqlite3_stmt *compiledStatement;
if(sqlite3_prepare_v2(database, [query UTF8String], -1, &compiledStatement, nil) == SQLITE_OK) {
NSLog(#"prepared OK");
while(sqlite3_step(compiledStatement) == SQLITE_ROW) {
//NSLog(#"reading row");
NSString *aNr = [NSString stringWithUTF8String:(char *)sqlite3_column_text(compiledStatement, 0)];
NSString *aTitle = [NSString stringWithUTF8String:(char *)sqlite3_column_text(compiledStatement, 1)];
NSString *aDescription = [NSString stringWithUTF8String:(char *)sqlite3_column_text(compiledStatement, 2)];
//NSLog(#"glyph: %# %# %#", aNr, aTitle, aDescription);
Glyph *g = [[Glyph alloc] initWithCharId:charId glyphNr:aNr glyphTitle:aTitle glyphDescription:aDescription];
[glyphs addObject:g];
[g release];
}
}
else {
NSLog(#"not prepared");
}
// Release the compiled statement from memory
sqlite3_finalize(compiledStatement);
}
else{
NSLog(#"sqlite not ok");
}
sqlite3_close(database);
NSLog(#"fin reading database2");
}
Ok, seems that the databasePath wasn't accessible, so i re-inited it and now works. If someone can enlighten a bit about why....
databasePath was inited in applicationDidFinishLaunching
Are you calling sqlite3_open() in each invocation of readGlyphsFromDatabaseAtId? If you're doing that with the database already open, that may cause issues.
Related
I am creating and iphone app using XCode 4.2. And I am using sqlite3 database for the app. I created and ran the app successfully on iPhone 3GS and with XCode 3.2.5, when I am having a problem with the XCode 4.2. The db file cannot open, here is the sample code code for opening the Table. And when I opened the same db file using SQlite manager, I could see the table. I don't understand what the error is.
static sqlite3 *database = nil;
static sqlite3_stmt *selectStmt = nil;
+ (void) getInitialDataToDisplay:(NSString *)dbPath {
NSLog(#"Path: %#",dbPath);
AppDelegate *appDelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];
if (sqlite3_open([dbPath UTF8String], &database) == SQLITE_OK) {
NSString *sqlStr = #"select * from Space";
const char *sql = [sqlStr UTF8String];
sqlite3_stmt *selectstmt;
if(sqlite3_prepare_v2(database, sql, -1, &selectstmt, NULL) == SQLITE_OK) {
while(sqlite3_step(selectstmt) == SQLITE_ROW) {
NSInteger primaryKey = sqlite3_column_int(selectstmt, 0);
SpaceClass *spaceObj = [[SpaceClass alloc] initWithPrimaryKey:primaryKey];
spaceObj.spacePK = [NSString stringWithUTF8String:(char *)sqlite3_column_text(selectstmt, 1)];
spaceObj.spName = [NSString stringWithUTF8String:(char *)sqlite3_column_text(selectstmt, 3)];
spaceObj.spDescrptn = [NSString stringWithUTF8String:(char *)sqlite3_column_text(selectstmt, 4)];
[appDelegate.spaceArray addObject:spaceObj];
[spaceObj release];
}
}else
NSLog(#"not ok");
}
else
sqlite3_close(database); //Even though the open call failed, close the database connection to release all the memory.
}
Please help, thanks
You put the close method in the wrong place I think. I have been using SQLite3 in iOS for 2 weeks and I had that problem. I solved it by putting the SQLite3_close method in the last line of the if(open == ok).
Your code should look like:
if (sqlite3_open([dbPath UTF8String], &database) == SQLITE_OK)
{
if(sqlite3_prepare_v2(database, sql, -1, &selectstmt, NULL) == SQLITE_OK)
{
while(sqlite3_step(selectstmt) == SQLITE_ROW)
{
}
}
else
{
NSLog(#"not ok");
}
//here you should close database, before exit from if open block
sqlite3_close(database);
}
else
{
//here is not needed because of database open failure
//sqlite3_close(database);
NSLog(#"not ok");
}
This should solve your problem because now you're going to close the database each time you open it. But in your code you open it time after time without close it!
I have a massive problem with SQLite in my iPhone app, that needs to be fixed for a client soon! Basically i'm pulling a column from a database and loading it into a table view on viewWillAppear. It works fine, for the first few times the view is loaded but then suddenly it starts return empty (null) values. Upon inspection it appears that there is an issue with opening the database maybe but this is my first SQLite project so it's hard to figure out what's going on.
Here is the code I use to pull the SQL information:
+ (void) getInitialDataToDisplay:(NSString *)dbPath {
NSLog(#"INCOME CALLED 1");
NavTabAppDelegate *appDelegate = (NavTabAppDelegate *)[[UIApplication sharedApplication] delegate];
appDelegate.incomeArray = [[NSMutableArray alloc] init];
if (sqlite3_open([dbPath UTF8String], &database) == SQLITE_OK) {
NSLog(#"INCOME CALLED 2");
const char *sql = "select IncomeID, IncomeName from Income ORDER BY IncomeName asc";
//const char *sql2 = "select categoryID, Size from coffee";
sqlite3_stmt *selectstmt;
if(sqlite3_prepare_v2(database, sql, -1, &selectstmt, NULL) == SQLITE_OK) {
NSLog(#"INCOME CALLED 3");
while(sqlite3_step(selectstmt) == SQLITE_ROW) {
NSLog(#"INCOME CALLED 4");
NSInteger primaryKey = sqlite3_column_int(selectstmt, 0);
Income *incomeObj = [[Income alloc] initWithPrimaryKey:primaryKey];
//This is how I pull info from the database using the above select statement and setting it in the coffeeObj property of Coffee class
incomeObj.incomeName = [NSString stringWithUTF8String:(char *)sqlite3_column_text(selectstmt, 1)];
//coffeeObj.coffeeSize = [NSString stringWithUTF8String:(char *)sqlite3_column_text(selectstmt, 2)];
incomeObj.isDirty = NO;
[appDelegate.incomeArray addObject:incomeObj];
[incomeObj release];
NSLog(#"INCOME OBJECTS %#", incomeObj.incomeName);
//NSLog(#"CALLED");
}
}
}
else{
sqlite3_close(database); //Even though the open call failed, close the database connection to release all the memory
NSLog(#"INCOME CALLED 5");
}
NSLog(#"INCOME CALLED 6");
}
I am calling the code in viewWillAppear as follows:
[Income getInitialDataToDisplay:[appDelegate getDBPath]];
Also this is the output from my console when the error occurs:
2011-06-17 12:21:48.307 CashCal[318:707] GET DB PATH CALLED
2011-06-17 12:21:48.310 CashCal[318:707] /var/mobile/Applications/2BD7CA1D-C7AB-4425-B5C1-974C4F4D057C/Documents/SQL.sqlite
2011-06-17 12:21:48.312 CashCal[318:707] INCOME CALLED 1
2011-06-17 12:21:48.314 CashCal[318:707] INCOME CALLED 5
2011-06-17 12:21:48.318 CashCal[318:707] INCOME CALLED 6
It appears the problem is with the first if statement opening the database. I really need help on this one
You should always destroy prepared statements by calling sqlite3_finalize(your_stmt). And you should always close youre connection not only if it sqlite3_open() failed.
Also do something like this:
if(sqlite3_prepare_v2(database, sql, -1, &selectstmt, NULL) == SQLITE_OK) {
//your code here
} else {
//print error
NSLog(#"sql-error: %s", sqlite3_errmsg(database));
}
for clarification, here a interface for your db, that should avoid opening the db when its allready open, and makes code cleaner
DBi.h
#import <Foundation/Foundation.h>
#import <sqlite3.h>
#interface DBi : NSObject {
sqlite3* db;
}
- (void)opendDB;
- (void)closeDB;
- (NSArray*)getIcons;
#end
DBi.m
#import "DBi.h"
#import "Income.h"
static DBi *sharedDBi = nil;
#implementation DBi
- (void)dealloc {
sqlite3_close(db);
[super dealloc];
}
+ (DBi*)sharedManager {
if (sharedDBi == nil) {
sharedDBi = [[super allocWithZone:NULL] init];
[sharedDBi opendDB];
}
return sharedDBi;
}
+ (id)allocWithZone:(NSZone *)zone {
return [[self sharedManager] retain];
}
- (void)opendDB {
NSString *dbPath = [[NSBundle mainBundle]pathForResource:#"path_to_db_file"ofType:#"sqlite"];
//open the database
if(!sqlite3_open([dbPath UTF8String], &db) == SQLITE_OK) {
NSLog(#"connection to db failed");
sqlite3_close(db);
}
}
- (void)closeDB {
sqlite3_close(db);
}
- (NSArray*)getIncoms {
NSMutableArray rArray = [[[NSMutableArray alloc] init] autorelease];
const char *sql = "select IncomeID, IncomeName from Income ORDER BY IncomeName asc";
//const char *sql2 = "select categoryID, Size from coffee";
sqlite3_stmt *selectstmt;
if(sqlite3_prepare_v2(db, sql, -1, &selectstmt, NULL) == SQLITE_OK) {
while(sqlite3_step(selectstmt) == SQLITE_ROW) {
NSInteger primaryKey = sqlite3_column_int(selectstmt, 0);
Income *incomeObj = [[Income alloc] initWithPrimaryKey:primaryKey];
incomeObj.incomeName = [NSString stringWithUTF8String:(char *)sqlite3_column_text(selectstmt, 1)];
incomeObj.isDirty = NO;
[rArray addObject:incomeObj];
[incomeObj release];
}
} else {
NSLog(#"sql-error in getIncoms: %s", sqlite3_errmsg(db));
}
sqlite3_finalize(selectstmt);
return rArray;
}
Now you could do something like that.
DBi *dbi = [[DBi alloc] init];
NSArray *incoms = [dbi getIncoms];
[dbi release];
hope, that should help
use this code
sqlite3 *database;
if(sqlite3_open([databasePath UTF8String], &database) == SQLITE_OK)
{
////your Code
if(sqlite3_prepare_v2(database, [sqlStatement cStringUsingEncoding:NSUTF8StringEncoding], -1, &compiledStatement, NULL) == SQLITE_OK)
{
NSLog(#"%#",sqlStatement);
while(sqlite3_step(compiledStatement) == SQLITE_ROW)
{
//your code
}
}
sqlite3_finalize(compiledStatement);
sqlite3_close(database);
}
}
Use FMDB for you sqlite tasks. It's a bad practice to use sqlite calls everywhere in the application. Just search on google for FMDB Example you will find one. FMDB is much easier to implement then SQLIte because FMDB handles all the things.
It turns out I was calling the same SQL method in two different places in very quick succession before the statements had a chance to be finalised each time. I guess this caused some sort of overlap eventually leading to the statements becoming invalid. Thanks for all the help. I will definitely be using CoreData or FMDB next time.
i try to get a connection my server, with the sqlite3_open command!
my question...is it possible to that? i got the following code...
// Get the path to the documents directory and append the databaseName
databaseName = #"AnimalDatabase.sql";
NSString *serverpath = #"http://localhost/app/";
databasePath = [serverpath stringByAppendingPathComponent:databaseName];
and then this here
-(void) readAnimalsFromDatabase {
// Setup the database object
sqlite3 *database;
// Init the animals Array
animals = [[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
const char *sqlStatement = "select * from animals";
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
NSString *aName = [NSString stringWithUTF8String:(char *)sqlite3_column_text(compiledStatement, 1)];
NSString *aDescription = [NSString stringWithUTF8String:(char *)sqlite3_column_text(compiledStatement, 2)];
NSString *aImageUrl = [NSString stringWithUTF8String:(char *)sqlite3_column_text(compiledStatement, 3)];
// Create a new animal object with the data from the database
Animal *animal = [[Animal alloc] initWithName:aName description:aDescription url:aImageUrl];
// Add the animal object to the animals Array
[animals addObject:animal];
[animal release];
}
}
// Release the compiled statement from memory
sqlite3_finalize(compiledStatement);
}
sqlite3_close(database);
}
any suggestion??
Where you get the idea that SQLite can open database from URL??
It can open files only (or, create temporary db in memory).
The answer is just NO, there are lot of mistakes in your code anyway. For example:
databaseName = #"AnimalDatabase.sql";
Where did you get this. iPhone is working with sqlite database, it has nothing in common with sql files:)
I have a table and 3 records.
and I have the code;
-(void) readScoreFromDatabase {
sqlite3 *database;
scores = [[NSMutableArray alloc] init];
if(sqlite3_open([databasePath UTF8String], &database) == SQLITE_OK) {
const char *sqlStatement = "select name,score from game";
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) {
//if(sqlite3_step(compiledStatement) == SQLITE_ROW) {
// Read the data from the result row
NSString *aName =[NSString stringWithUTF8String:(char *)sqlite3_column_text(compiledStatement, 0)];
NSString *aScore =[NSString stringWithUTF8String:(char *)sqlite3_column_text(compiledStatement, 1)];
DatabaseClass *dbOBJ = [[DatabaseClass alloc] initWithName:aName score:aScore];
[scores addObject:dbOBJ];
[dbOBJ release];
}
}
sqlite3_finalize(compiledStatement);
} else {
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Error" message:#"No Connection"
delegate:self cancelButtonTitle:#"OK" otherButtonTitles: nil];
[alert show];
}
sqlite3_close(database);
}
and I am using that code to show records;
-(IBAction)refreshClick:(id)sender {
// Navigation logic -- create and push a new view controller
IDRGameAppDelegate *appDelegate = (IDRGameAppDelegate *)[[UIApplication sharedApplication] delegate];
DatabaseClass *dbOBJ = (DatabaseClass *)[appDelegate.scores objectAtIndex:1];
game1NameLabel.text = dbOBJ.name;
score1Label.text = dbOBJ.score;
}
I have 3 records, but I can take only one record. I mean changed that line "DatabaseClass *dbOBJ = (DatabaseClass *)[appDelegate.scores objectAtIndex:1];"
objectAtIndex:1 when I change this value 1 or 2 or 3 etc. the result doesn't change. It is always showing one record from 3 records. I don't understand the reason.
Thank you.
Are you able to see the table rows, when you go to the SQLite manager (from Firefox) and run the below query:
select name,score from game;
If you are able to see 3 records, then:
Go to the iPhone simulator and reset settings
(which will clear all the temp caches, database, and build files).
Go to Xcode and do a Build and GO.
This will get you latest database; put it in the Build folder.
This may not be the answer you want, but you should really consider using Core Data now that it's in iPhone OS 3.0. It handles most of this for you and is very easy to use.
Maybe you forgot to set score to your delegate?
scores = [[NSMutableArray alloc] init];
....
//I don't see this in your code
((IDRGameAppDelegate *)[[UIApplication sharedApplication] delegate]).score = score;
I am having a problem with what I believe is a memory leak which after some time is causing my app to slow down. I have a sectioned uitableview that has sections of 'movie directors' with rows of thier movies in their particular section. To do this I am calling a data object (and passing it the section header) to return that section's data and populate the section rows. So I am calling that object a few times on the same view(numberOfRowsInSection, cellForRowAtIndexPath, and didSelectRowAtIndexPath) this happens for each section. Looking at Instruments, I believe the leak is coming from getDirectorsMovies:theDirector from Movies.m. Can anyone tell me what I am doing that is causing this leak. Any help would be greatly appreciated, I've been working on this for a few weeks. Below is some code to show what I am doing.
Thanks in advance!!!
//Movies.h
#import <Foundation/Foundation.h>
#import <sqlite3.h>
#import "Movie.h"
#interface Movies : NSObject {
}
- (NSMutableArray *) getDirectorsMovies:(NSString *)theDirector;
#end
//Movies.m //getDirectorsMovies:(NSString *)theDirector goes to the database, gets the directors movies, and returns them in an array
#import "Movies.h"
#implementation Movies
- (NSMutableArray *) getDirectorsMovies:(NSString *)theDirector
{
sqlite3 *database;
NSString *databaseName = #"Movies.sql";
NSArray *documentPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDir = [documentPaths objectAtIndex:0];
NSString *databasePath = [documentsDir stringByAppendingPathComponent:databaseName];
NSMutableArray *theDirectorsMovies = [[NSMutableArray alloc] init];
if(sqlite3_open([databasePath UTF8String], &database) == SQLITE_OK) {
const char *sqlStatement = "select * from movies where lastname = ? order by lastname, movie";
sqlite3_stmt *compiledStatement;
if(sqlite3_prepare_v2(database, sqlStatement, -1, &compiledStatement, NULL) == SQLITE_OK) {
sqlite3_bind_text(compiledStatement, 1, [theDirector UTF8String], -1, SQLITE_TRANSIENT);
while(sqlite3_step(compiledStatement) == SQLITE_ROW) {
NSString *aLastName = [NSString stringWithUTF8String:(char *)sqlite3_column_text(compiledStatement, 1)];
NSString *aDirector = [NSString stringWithUTF8String:(char *)sqlite3_column_text(compiledStatement, 2)];
NSString *aMovie = [NSString stringWithUTF8String:(char *)sqlite3_column_text(compiledStatement, 3)];
Movie *movie = [[Movie alloc] initWithName:aMovie lastname:aLastName director:aDirector];
[theDirectorsMovies addObject:movie];
[movie release];
}
}
sqlite3_finalize(compiledStatement);
}
sqlite3_close(database);
return theDirectorsMovies;
[theDirectorsMovies release];
}
#end
//Calling getDirectorsMovies:(NSString *)theDirector
MoviesAppDelegate *appDelegate = (MoviesAppDelegate *)[[UIApplication sharedApplication] delegate];
Director *director = (Director *)[appDelegate.director objectAtIndex:indexPath.section];//appDelegate.director IS A MSMutableArray defined in the AppDelegate
self.theMovies = nil;//THIS IS A MSMutableArray defined in the AppDelegate
Movies *directorMovies = [[Movies alloc] init];
self.theMovies = [directorMovies getDirectorMovies:director.lastname];
[directorMovies release];
Movie *movie = (Movie *)[theMovies objectAtIndex:indexPath.row];
//do whatever with the data
[movie release];
You have this:
return theDirectorsMovies;
[theDirectorsMovies release];
Nothing happens after the return statement, so your call to release will never happen. It was for this very reason that AutoreleasePools were invented (and patented). Simply do:
return [theDirectorsMovies autorelease];
And your memory leak will go away.
There are a number of potential issues here it would be useful to see the init method for Movie as that may be where the issue is. Also you should have an autorelease on the NSMutableArray.
In my sqlite code I have the following statement normally before the sqlite3_finalize call
sqlite3_clear_bindings(compiledStatement);
That call cleared up a lot of issues for my code.