if I try and reun the code below I get an EXE_bad_access message on [categoryList count]
NSMutableArray *categoryList = [[CategoryItem alloc] getAll];
NSLog(#"number of items is %#", [categoryList count]);
The class is below
#import "CategoryItem.h"
#import "SQLite.h"
#interface CategoryItem : NSObject {
NSInteger ID;
NSInteger SortOrder;
NSString *Name;
NSString *ShoppingImage;
}
#property (nonatomic, nonatomic) NSInteger SortOrder;
#property (nonatomic, retain) NSString * Name;
#property (nonatomic, retain) NSString * ShoppingImage;
#property (nonatomic, nonatomic) NSInteger ID;
- (id)initWithObject:(NSInteger)itemID;
-(NSMutableArray *)getAll;
#end
#implementation CategoryItem
#synthesize ShoppingImage;
#synthesize Name;
#synthesize ID;
#synthesize SortOrder;
- (id)initWithObject:(NSInteger)itemID {
if ((self = [super init])) {
sqlite3 *database;
// Open the database. The database was prepared outside the application.
if (sqlite3_open([[SQLite fullFilePath] UTF8String], &database) == SQLITE_OK) {
// Get the primary key for all books.
const char *sql = "SELECT ID, Name, ShoppingImage, SortOrder FROM CategoryItem WHERE ID =?";
sqlite3_stmt *statement;
// Preparing a statement compiles the SQL query into a byte-code program in the SQLite library.
// The third parameter is either the length of the SQL string or -1 to read up to the first null terminator.
if (sqlite3_prepare_v2(database, sql, -1, &statement, NULL) == SQLITE_OK) {
// We "step" through the results - once for each row.
sqlite3_bind_int(statement, 1, itemID);
while (sqlite3_step(statement) == SQLITE_ROW) {
// The second parameter indicates the column index into the result set.
self.ID = itemID;
self.Name = [NSString stringWithUTF8String:(char *)sqlite3_column_text(statement, 1)];
self.ShoppingImage = [NSString stringWithUTF8String:(char *)sqlite3_column_text(statement, 2)];
self.SortOrder = sqlite3_column_int(statement, 3);
}
}
// "Finalize" the statement - releases the resources associated with the statement.
sqlite3_finalize(statement);
} else {
// Even though the open failed, call close to properly clean up resources.
sqlite3_close(database);
NSLog(#"Failed to open database with message '%s'.", sqlite3_errmsg(database));
// Additional error handling, as appropriate...
}
}
return self;
}
-(NSMutableArray*)getAll{
NSMutableArray *listArray = [[[NSMutableArray alloc] init] autorelease];
sqlite3 *database;
// Open the database. The database was prepared outside the application.
if (sqlite3_open([[SQLite fullFilePath] UTF8String], &database) == SQLITE_OK) {
// Get the primary key for all books.
const char *sql = "SELECT ID, Name, ShoppingImage, SortOrder FROM CategoryItem ORDER BY SortOrder";
sqlite3_stmt *statement;
// Preparing a statement compiles the SQL query into a byte-code program in the SQLite library.
// The third parameter is either the length of the SQL string or -1 to read up to the first null terminator.
if (sqlite3_prepare_v2(database, sql, -1, &statement, NULL) == SQLITE_OK)
{
// We "step" through the results - once for each row.
while (sqlite3_step(statement) == SQLITE_ROW)
{
// The second parameter indicates the column index into the result set.
CategoryItem *categoryItem = [[CategoryItem alloc] init];
categoryItem.ID = sqlite3_column_int(statement, 0);
categoryItem.Name = [NSString stringWithUTF8String:(char *)sqlite3_column_text(statement, 1)];
categoryItem.ShoppingImage = [NSString stringWithUTF8String:(char *)sqlite3_column_text(statement, 2)];
categoryItem.SortOrder = sqlite3_column_int(statement, 3);
[listArray addObject:categoryItem];
[categoryItem release];
categoryItem = nil;
}
}else{
printf( "could not prepare statemnt: %s\n", sqlite3_errmsg(database) );
}
// "Finalize" the statement - releases the resources associated with the statement.
sqlite3_finalize(statement);
} else {
// Even though the open failed, call close to properly clean up resources.
sqlite3_close(database);
NSLog(#"Failed to open database with message '%s'.", sqlite3_errmsg(database));
// Additional error handling, as appropriate...
}
//NSLog(#"this is the list array count %#", [listArray count]);
return listArray;
}
- (void)dealloc {
[super dealloc];
[Name release];
[ShoppingImage release];
}
#end
It doesn't seem right the way you create your CategoryItem. You are calling allocbut not any init... method. You may want to use the initWithObject method that you have provided in your implementation.
From Apple docs:
It takes two steps to create an object
using Objective-C. You must:
Dynamically allocate memory for the
new object
Initialize the newly
allocated memory to appropriate values
An object isn’t fully functional until
both steps have been completed. Each
step is accomplished by a separate
method but typically in a single line
of code:
id anObject = [[Rectangle alloc]
init];
EDIT:
Beyond the initialization problem, there seems to be a conceptual problem (pointed out by #Terry Wilcox):
Calling the method getAllon an instance does not seem to make sense and therefore should be defined as a class method instead:
+ (NSMutableArray*)getAll;
and should be called like this:
NSMutableArray *categoryList = [CategoryItem getAll];
EDIT 2:
Your log statement does not seem right either. [categoryList count]returns a NSUIntegerand you are trying to print an object with %#. Use %iinstead:
NSLog(#"number of items is %i", [categoryList count]);
This code:
NSMutableArray *categoryList = [[CategoryItem alloc] getAll];
doesn't make sense. If getAll is a class method on CategoryItem, then it should be defined as
+ (NSMutableArray*)getAll;
and you should call it as
NSMutableArray *categoryList = [CategoryItem getAll];
Then categoryList will be an array that you don't own, so you may want to retain it when you get it.
Related
I have a database manager which successfully loads a string from my sqlite database, and then uses it to create an object. I add multiple of these objects to an array and return it to which ever class I'm calling it from.
I have no problem using the returned array in the method I call it from, but when other methods in the class try to access it, the string element is out of scope.
Here is my relavent code.
In DBManager.m:
-(NSArray *)loadObjectData {
NSMutableArray *myArray = [[NSMutableArray alloc] init];
//prepare query and statement
NSString *query = [NSString stringWithFormat:#"SELECT * FROM object;"];
sqlite3_stmt *statement;
//perform query
if (sqlite3_prepare_v2(database, [query UTF8String], -1, &statement, nil) != SQLITE_OK)
NSLog(#"Prepare Error. '%s'", sqlite3_errmsg(database));
//get data
while (sqlite3_step(statement) == SQLITE_ROW) {
//read values
int anInt = sqlite3_column_int(statement, 0);
NSString *aString = [NSString stringWithUTF8String:(char *)sqlite3_column_text(statement, 1)];
//add new object to list
[myArray addObject:[[Object alloc] initWithID:anInt Name:aString]];
}
if(sqlite3_finalize(statement) != SQLITE_OK)
NSLog(#"Finalize statement error.");
return [myArray autorelease];
}
In XViewController.h:
NSArray *listData;
SessionData *sessionData;
#property (nonatomic, retain) NSArray *listData;
#property (nonatomic, retain) SessionData *sessionData;
In XViewController.m:
- (void)viewDidLoad
{
dbMan = [DBManager sharedDBMananager]; //its a singleton
//Load objects to display in table
self.listData = [dbMan loadObjectData];
//if i try to use listData here, it works fine.
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
//normal stuff cut out
// Configure the cell...
//the following line fails because the string in the object out of scope
cell.textLabel.text = [[listData objectAtIndex:[indexPath row]] aString];
//trying to access [[listData objectAtIndex:[indexPath row]] anInt] works fine though.
return cell;
}
If I replace the line (in my loadObjectData method):
NSString *aString = [NSString stringWithUTF8String:(char *)sqlite3_column_text(statement, 2)];
with this:
NSString *aString = #"String";
everything works fine.
Take a look at this question's accepted answer - What does assigning a literal string to an NSString with "=" actually do?
The string literal ( #"String" ) works because it is statically allocated and does not need to be retained/released. [NSString stringWithUTF8String:] creates an autoreleased string. If you are not retaining it in your initWithID:Name: method then it is being released which is why your code isn't working. In your initWithID:Name: method at a retain when you reassign the name to your object's instance variable.
In Object class have you retained this string if not then i think you should and release this string in dealloc
I am building an app which contains a form in one view,in which the user fills all the fields and when he clicks the save button the data must be saved in to database and after navigating back,there's another view which, when entered, must show the saved data(event).
I have created a database and have gone through several sqlite3 tutorials;
I have done all other changes to my code according to my requirement. However, when I use this statement to check whether data is inserted in database:
SELECT * FROM reminders;
I am getting nothing and I am confused whether data is inserted or not.
How do I save it properly, and how do I retrieve data from database to use and display it in other view?
First you should create the sqlite3 database file (check this link), then you should include it into your project. Now to connect to it you can use the following code:
#pragma mark -
#pragma mark Create/Load Database
+ (void)createEditableCopyOfDatabaseIfNeeded {
// First, test for existence.
NSArray * paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString * documentsDirectory = [paths objectAtIndex:0];
NSString * writableDBPath = [documentsDirectory stringByAppendingPathComponent:#"DATABASENAME.DB"];
BOOL success;
NSFileManager * fileManager = [NSFileManager defaultManager];
success = [fileManager fileExistsAtPath:writableDBPath];
if (success) {
return;
}
// The writable database does not exist, so copy the default to the appropriate location.
NSError * error;
NSString * defaultDBPath = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:#"DATABASENAME.DB"];
success = [fileManager copyItemAtPath:defaultDBPath toPath:writableDBPath error:&error];
if (!success) {
NSAssert1(0, #"Failed to create writable database file with message '%#'.", [error localizedDescription]);
}
}
+ (sqlite3 *)getDBConnection {
[DatabaseController createEditableCopyOfDatabaseIfNeeded];
NSArray * paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString * documentsDirectory = [paths objectAtIndex:0];
NSString * path = [documentsDirectory stringByAppendingPathComponent:#"DATABASENAME.DB"];
// Open the database. The database was prepared outside the application.
sqlite3 * newDBConnection;
if (sqlite3_open([path UTF8String], &newDBConnection) == SQLITE_OK) {
//NSLog(#"Database Successfully Opened :)");
} else {
//NSLog(#"Error in opening database :(");
}
return newDBConnection;
}
then to insert a record you can use this code:
+ (void)insertEvent:(Event *)newEvent {
sqlite3 * connection = [DatabaseController getDBConnection];
const char * text = "INSERT INTO Event (Serial, Name, Date) VALUES (?, ?, ?)";
sqlite3_stmt * insert_statement;
int prepare_result = sqlite3_prepare_v2(connection, text, -1, &insert_statement, NULL);
if ((prepare_result != SQLITE_DONE) && (prepare_result != SQLITE_OK)) {
// Error
sqlite3_close(connection);
return;
}
sqlite3_bind_int(insert_statement, 1, newEvent.Serial);
sqlite3_bind_text(insert_statement, 2, [newEvent.Name UTF8String], -1, SQLITE_TRANSIENT);
sqlite3_bind_double(insert_statement, 3, [newEvent.Date timeIntervalSince1970]);
int statement_result = sqlite3_step(insert_statement);
if ((statement_result != SQLITE_DONE) && (statement_result != SQLITE_OK)) {
//Error
sqlite3_close(connection);
return;
}
sqlite3_finalize(insert_statement);
// Get the Id of the inserted event
int rowId = sqlite3_last_insert_rowid(connection);
newEvent.Id = rowId;
sqlite3_close(connection);
}
now to get an event:
+ (Event *)getEventById:(int)id {
Event * result = nil;
sqlite3 * connection = [DatabaseController getDBConnection];
const char * text = "SELECT * FROM Event WHERE Id = ?";
sqlite3_stmt * select_statement;
int prepare_result = sqlite3_prepare_v2(connection, text, -1, &select_statement, NULL);
if ((prepare_result != SQLITE_DONE) && (prepare_result != SQLITE_OK)) {
// error
sqlite3_close(connection);
return result;
}
sqlite3_bind_int(select_statement, 1, id);
if (sqlite3_step(select_statement) == SQLITE_ROW) {
result = [[[Event alloc] init] autorelease];
result.Id = sqlite3_column_int(select_statement, 0);
result.Serial = sqlite3_column_int(select_statement, 1);
result.Name = (((char *) sqlite3_column_text(select_statement, 2)) == NULL)? nil:[NSString stringWithUTF8String:((char *) sqlite3_column_text(select_statement, 2))];
result.Date = [NSDate dateWithTimeIntervalSince1970:sqlite3_column_double(select_statement, 3)];
}
sqlite3_finalize(select_statement);
sqlite3_close(connection);
return (result);
}
Here is a blog post that should get you pointed in the right direction, pretty useful to me so sharing it with you.:P
http://dblog.com.au/iphone-development-tutorials/iphone-sdk-tutorial-reading-data-from-a-sqlite-database/
you can check whether your data has been saved or not by checking the database table. Go to Users>your computer name>Library>Application support>iphone Simulator>4.3(your version of ios)>Application.. Then look for your Application,go to documents and open the sqlite file. Here you can see the data.
You should use FMDB to reduce the complexity of your code.
It is an Objective-C wrapper around SQLite.
FMDB on github
This code used for storing data and retriving data from sqlite data base
First you just add sqlite3 frame work after that write bellow code in objective-c
ViewController.h
#import <UIKit/UIKit.h>
#import "sqlite3.h"
#interface ViewController : UIViewController
#property (weak, nonatomic) IBOutlet UITextField *firstName;
#property (weak, nonatomic) IBOutlet UITextField *lastName;
#property (weak, nonatomic) IBOutlet UITextField *state;
#property (weak, nonatomic) IBOutlet UITextField *mobileNum;
- (IBAction)saveButton:(id)sender;
- (IBAction)featchButton:(id)sender;
#property (weak, nonatomic) IBOutlet UILabel *label;
#property NSString *myDatabase;
#property sqlite3 *marksDB;
ViewController.m
#import "ViewController.h"
#interface ViewController ()
#end
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
NSString *docsDir;
NSArray *dirPaths;
dirPaths=NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
docsDir=dirPaths[0];
_myDatabase=[[NSString alloc]initWithString:[docsDir stringByAppendingString:#"marks.db"]];
NSLog(#"My Data base %#",_myDatabase);
NSFileManager *fileMgr=[NSFileManager defaultManager];
if ([fileMgr fileExistsAtPath:_myDatabase]==NO)
{
const char *dbpath=[_myDatabase UTF8String];
if (sqlite3_open(dbpath, &_marksDB)==SQLITE_OK)
{
char *errMsg;
const char *sql_stmt="CREATE TABLE IF NOT EXISTS MARKS(ID INTEGER PRIMARY KEY AUTOINCREMENT ,FIRST NAME TEXT,LAST NAME TEXT,STATE TEXT,MOBILE INTEGER )";
if (sqlite3_exec(_marksDB, sql_stmt, NULL, NULL, &errMsg)!=SQLITE_OK)
{
_label.text=#"Failed to create Table";
}
sqlite3_close(_marksDB);
}
else
{
_label.text=#"Failed to Create/Open Database";
}
}
}
- (IBAction)saveButton:(id)sender {
sqlite3_stmt *statement;
const char *dbpath=[_myDatabase UTF8String];
if (sqlite3_open(dbpath, &_marksDB)==SQLITE_OK)
{
NSString *insertSQL=[NSString stringWithFormat:#"INSERT INTO MARKS(firstname,lastname,state,mobile )VALUES(\"%#\",\"%#\",\"%#\",\"%#\")",_firstName.text,_lastName.text,_state.text,_mobileNum.text ];
const char *insert_stmt=[insertSQL UTF8String];
sqlite3_prepare_v2(_marksDB, insert_stmt, -1, &statement, NULL);
if (sqlite3_step(statement)==SQLITE_DONE)
{
_label.text=#"Contact Added";
_firstName.text=#"";
_lastName.text=#"";
_state.text=#"";
_mobileNum.text=#"";
}
else
{
_label.text=#"Failed to Add Contact";
}
sqlite3_finalize(statement);
sqlite3_close(_marksDB);
}
}
- (IBAction)featchButton:(id)sender {
const char *dbpath=[_myDatabase UTF8String];
sqlite3_stmt *statement;
if (sqlite3_open(dbpath, &_marksDB)==SQLITE_OK)
{
NSString *query=[NSString stringWithFormat:#"SELECT firstname,lastname,state,mobile, FROM MARKS WHERE firstname=\"%#\"",_firstName.text];
const char *query_stmt=[query UTF8String];
if (sqlite3_prepare_v2(_marksDB, query_stmt, -1, &statement, NULL)==SQLITE_OK)
{
if (sqlite3_step(statement)==SQLITE_ROW)
{
NSString *first=[[NSString alloc]initWithUTF8String:(const char *)sqlite3_column_text(statement, 0)];
_firstName.text=first;
NSString *lastName=[[NSString alloc]initWithUTF8String:(const char *)sqlite3_column_text(statement, 1)];
_lastName.text=lastName;
NSString *state=[[NSString alloc]initWithUTF8String:(const char *)sqlite3_column_text(statement, 2)];
_state.text=state;
NSString *mobile=[[NSString alloc]initWithUTF8String:(const char *)sqlite3_column_text(statement, 3)];
_mobileNum.text=mobile;
_label.text=#"Match Found";
}
else
{
_label.text=#"Not Matched";
_lastName.text=#"";
_state.text=#"";
_mobileNum.text=#"";
}
sqlite3_finalize(statement);
}
sqlite3_close(_marksDB);
}
}
#end
I'm reading data from a sqlite database however in my detailed view one of the data items is not showing correctly. It show what looks like random memory i.e. UIDeviceRGB...(I have two strings, one that works one that doesn't). I can't work out why but when I run the debug it returns null.
Here is my code in HydratedDetailData:
if (SQLITE_DONE != sqlite3_step(detailStmt)) {
double add = sqlite3_column_double(detailStmt, 2);
NSString *address = [NSString stringWithFormat:#"%#", add];
self.ClubAddress = address;
[address release];
}
Any help work be greatly appreciated!
code from Clubs.m
#import "Clubs.h"
static sqlite3 *database = nil;
static sqlite3_stmt *detailStmt = nil;
#implementation Clubs
#synthesize clubID, clubName, ClubAddress, isDirty, isDetailViewHydrated;
+(void) getInitialDataToDisplay:(NSString *)dbPath {
SQLAppDelegate *appDelegate = (SQLAppDelegate *) [[UIApplication sharedApplication] delegate];
if (sqlite3_open([dbPath UTF8String], &database) == SQLITE_OK) {
const char *sql = "select clubID, clubName from clubNames";
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);
Clubs *clubObj = [[Clubs alloc] initWithPrimaryKey:primaryKey];
clubObj.clubName = [NSString stringWithUTF8String:(char *)sqlite3_column_text(selectstmt, 1)];
clubObj.isDirty = NO;
[appDelegate.clubArray addObject:clubObj];
[clubObj release];
}
}
}
else
sqlite3_close(database);
}
+(void) finalizeStatements {
if (database) sqlite3_close(database);
}
-(id) initWithPrimaryKey:(NSInteger) pk {
[super init];
clubID = pk;
isDetailViewHydrated = NO;
return self;
}
-(void) hydrateDetailViewData {
if (isDetailViewHydrated) return;
if (detailStmt == nil) {
const char *sql = "Select ClubAddress from clubNames Where clubID = ?";
if (sqlite3_prepare_v2(database, sql, -1, &detailStmt, NULL) !=SQLITE_OK)
NSAssert1(0, #"Error while creating detail view statment. '%s'", sqlite3_errmsg(database));
}
sqlite3_bind_int(detailStmt, 1, clubID);
if (SQLITE_DONE != sqlite3_step(detailStmt)) {
double add = sqlite3_column_double(detailStmt, 0);
NSString *address = [NSString stringWithFormat:#"%d", add];
self.ClubAddress = address;
}
else
NSAssert1(0, #"Error while getting the address of club. '%s'", sqlite3_errmsg(database));
sqlite3_reset(detailStmt);
isDetailViewHydrated = YES;
}
-(void) dealloc {
[ClubAddress release];
[clubName release];
[super dealloc];
}
#end
i had such a problem for reading double values, some of values read right but some read as 0 ! i have tried many things but at last my problem was solved by defining variables globally. i have defined the locally in .m file but the solution was to define them in .h file.i hope it can solve yours too.
good luck
Perhaps if you used the %d formatter in your [NSString stringWithFormat:#"%#", add]; call.
(%# is generally for strings, %d is for doubles)
After our little discussion-athon, this should fix what ails you.
if (SQLITE_DONE != sqlite3_step(detailStmt)) {
char *db_text = sqlite3_column_text(detailStmt, 2);
NSString *address = [NSString stringWithUTF8String: db_text];
self.ClubAddress = address;
}
This will pull the text value from the database and convert it into an NSString*. (hopefully)....
I have an NSObject that I am using to store/hold properties of an object, one of them being a "Name" property cast as an NSString. I am also pulling data from a SQLite DB for said object using the following:
- (void) getDataToDisplay:(NSString *)dbPath {
if (sqlite3_open([dbPath UTF8String], &database) == SQLITE_OK) {
NSString *queryOnStyle = [NSString stringWithFormat:
#"SELECT WineId, Name FROM wine WHERE StyleId = %d", dataManager.styleId];
const char *sql = [queryOnStyle UTF8String];
sqlite3_stmt *selectstmt;
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)];
[dataManager.wines addObject:w];
[w release];
}
}
}
else
sqlite3_close(database); //Even though the open call failed, close the database connection to release all the memory.
}
Wine being my object. If I were to log w.wineName at this point, there is no problem. The problem occurs later when I try to access the properties of the object from within the array, dataManager.wines, in a custom tableView. It suddenly treats my wineName as a UIImageView rather than an NSString...
I for the life of me cannot trace anything back to ever being cast as a UIImageView and have no idea why it would be setting just that property as such. Here is the code for my custom tableView:
#pragma mark -
#pragma mark HorizontalTableViewDelegate methods
- (NSInteger)numberOfColumnsForTableView:(HorizontalTableView *)tableView {
return [dataManager.wines count];
}
- (UIView *)tableView:(HorizontalTableView *)aTableView viewForIndex:(NSInteger)index {
UIView *vw = [aTableView dequeueColumnView];
if (!vw) {
[[NSBundle mainBundle] loadNibNamed:#"ColumnView" owner:self options:nil];
vw = self.columnView;
self.columnView = nil;
}
// Get the wineId from the array of wineId integers
Wine *w = [dataManager.wines objectAtIndex:index];
int tempWineId = w.wineId;
NSString *tempWineName = [NSString stringWithFormat:#"%#", w.wineName];
NSLog(#"%#", tempWineName); \\RETURNS TEMPWINENAME AS A UIIMAGEVIEW
[w release];
return vw;
}
- (CGFloat)columnWidthForTableView:(HorizontalTableView *)tableView {
//TODO: This value needs to change if changed in IB
return 209.0f;
}
any ideas?
Resolved in comments of question by RichB:
is w.wineName a retain'ed property ? This sounds a bit like the string is being auto released. Could you post the definition of the Wine object?
I was successfully accessing my database to get a list of cities on the App launch. I tried running a second query against it right afterward to get the list of States but all that happens is that my app blows up with no usable error in the console (simply says "Program received signal: EXEC_BAD_ACCESS" and nothing more).
Here is the code, I was hoping someone could potentially explain to me what I'm doing wrong:
-(void) initializeDatabase{
// The database is stored in the application bundle
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *path = [documentsDirectory stringByAppendingPathComponent:#"mydatabase.sqlite"];
// Open the database. The database was prepared outside the application.
if (sqlite3_open([path UTF8String], &database) == SQLITE_OK){
[self initializeCities:database];
[self initializeStates:database];
} 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...
}
}
-(void) initializeCities:(sqlite3 *)db {
NSMutableArray *cityArray = [[NSMutableArray alloc] init];
self.cities = cityArray;
[cityArray release];
// Get the primary key for all cities.
const char *sql = "SELECT id FROM my_table ORDER BY state";
sqlite3_stmt *statement;
if (sqlite3_prepare_v2(db, sql, -1, &statement, NULL) == SQLITE_OK){
while (sqlite3_step(statement) == SQLITE_ROW){
int primaryKey = sqlite3_column_int(statement, 0);
City *city = [[City alloc] initWithPrimaryKey:primaryKey database:db];
[cities addObject:city];
[city release];
}
}
// "Finalize" the statement - releases the resources associated with the statement.
sqlite3_finalize(statement);
}
-(void) initializeStates:(sqlite3 *)db {
NSMutableArray *statesArray = [[NSMutableArray alloc] init];
self.states = statesArray;
[statesArray release];
// Get the primary key for all cities.
const char *sql = "SELECT DISTINCT state FROM my_table ORDER BY state";
sqlite3_stmt *statement;
if (sqlite3_prepare_v2(db, sql, -1, &statement, NULL) == SQLITE_OK){
// We "step" through the results - once for each row
while (sqlite3_step(statement) == SQLITE_ROW){
NSString *state;
state = (NSString *)sqlite3_column_text(statement, 0);
[states addObject:state];
[state release];
}
}
// "Finalize" the statement - releases the resources associated with the statement.
sqlite3_finalize(statement);
}
I can't debug this code as the debugger never hits my breakpoints at all.
If I remove the initializeStates method the app works as expected (albiet without a list of states).
You are releasing "state" without having allocated it. Try something like this:
while (sqlite3_step(statement) == SQLITE_ROW){
NSString *state = [[NSString alloc] initWithCString:(char*)sqlite3_column_text(statement, 0) encoding:NSASCIIStringEncoding];
//state = (NSString *)sqlite3_column_text(statement, 0);
[states addObject:state];
[state release];
}
Update: add cast above to fix compiler warning
Your problem is this:
NSString *state = (NSString *)sqlite3_column_text(statement, 0);
According to the documentation, sqlite3_column_text() returns a char*, not an NSString*.
Edit: You wouldn't have had this problem if you'd have used a wrapper ;)