I have an SQLite Database with 5000 rows that is loaded into a tableview.
It takes around 5-10 sec (which is extremely long on the iPhone) to load the table. Is there a way I can load the cells in smaller batches or something else to speed this up? Also, when I go back up in hierarchy then revisit this view, it has to reload all over again. Can I cache this out instead?
- (void)viewDidLoad
{
[super viewDidLoad];
[NSThread detachNewThreadSelector:#selector(myThread:) toTarget:self withObject:nil];
}
-(void) myThread:(id) obj {
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
NSMutableArray *array = [[Database sharedDatabase] getICD9DiseaseCodes];
[self performSelectorOnMainThread:#selector(dataDoneLoading:) withObject:array waitUntilDone:NO];
[pool release];
}
-(void) dataDoneLoading:(id) obj {
NSMutableArray *array = (NSMutableArray *) obj;
self.codeAray = array;
[self.myTableView reloadData];
[self dismissHUD];
NSLog(#"done");
}
In my database class:
-(NSMutableArray *) getICD9DiseaseCodes
{
sqlite3 *database = CodesDatabase();
NSMutableArray *icd9DiseaseCodes = [[[NSMutableArray alloc] init] autorelease];
NSString *nsquery = [[NSString alloc] initWithFormat:#"SELECT code, title, description FROM icd9_disease_codes ORDER BY code"];
const char *query = [nsquery UTF8String];
[nsquery release];
sqlite3_stmt *statement;
int prepareCode = (sqlite3_prepare_v2( database, query, -1, &statement, NULL));
if(prepareCode == SQLITE_OK) {
while (sqlite3_step(statement) == SQLITE_ROW)
{
ICD9DiseaseCodes *code = [[ICD9DiseaseCodes alloc] init];
code.code = [NSString stringWithCString:(char *)sqlite3_column_text(statement, 0) encoding:NSUTF8StringEncoding];
code.name = [NSString stringWithCString:(char *)sqlite3_column_text(statement, 1) encoding:NSUTF8StringEncoding];
code.description = [NSString stringWithCString:(char *)sqlite3_column_text(statement, 2) encoding:NSUTF8StringEncoding];
[icd9DiseaseCodes addObject:code];
// NSLog(#"Code: %# Short Description: %# Long Description: %#", hcpcsCode.code, hcpcsCode.name, hcpcsCode.description);
[code release];
}
sqlite3_finalize(statement);
}
return icd9DiseaseCodes;
}
My first question is why do you use SQLite directly, and not Core Data? Core Data will do fast partial fetches from the data store for you, for free with no extra code on your part.
If you want to do the same thing manually with SQLite directly, then you are in for some heavy lifting code. You will need to use LIMIT and OFFSET in your SQL queries to fetch partial data, and extend your API to fetch only smaller sub-sets at a time.
Have you considered or looked into CoreData? CoreData has a lot of optimization for large recordsets.
Thanks.
James
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
I want to load data to uipickerview from database.
this is my code
if (sqlite3_prepare_v2(UsersDB, query_stmt, -1, &statement, NULL) == SQLITE_OK)
{
int i=0;
while (sqlite3_step(statement) == SQLITE_ROW)
{
NSLog(#"select");
self.coun=[[NSString alloc] initWithUTF8String:(char *)sqlite3_column_text(statement, 0)];
NSLog(#"Coun = %#",coun);
self.animals=[coun componentsSeparatedByString:#":"];
NSLog(#"String value=%#",self.animals);
}
sqlite3_finalize(statement);
} else
{
NSLog(#"not select");
}
and button actions code is
- (IBAction)selectAnItem:(UIControl *)sender {
//Display the ActionSheetPicker
[ActionSheetPicker displayActionPickerWithView:sender data:self.animals selectedIndex:self.selectedIndex target:self action:#selector(itemWasSelected::) title:#"Select Country"];
}
but it run load only last value. Not all values loaded in UIpickerview. How to load all values from database using this code. Anyone pls help me. Thanks in advance.
You have to do like this,
NSArray *splitedString = [coun componentsSeparatedByString: #":"];
NSString *animalName;
if ([splitedString count]>0)
{
animalName=[splitedString objectAtIndex:0];
[self.animals addObject:animalName]; //here animals should be NSMutableArray
}
In the while loop, you have to collect all the self.coun values in an array. When you come out of the loop, having all the values, populate the picker.
NSMutableArray *countries = [[NSMutableArray alloc] init];
while (sqlite3_step(statement) == SQLITE_ROW){
self.coun=[[NSString alloc] initWithUTF8String:(char *)sqlite3_column_text(statement, 0)];
NSLog(#"Coun = %#",coun);
[countries addObject:coun];
//self.animals=[coun componentsSeparatedByString:#":"];
//NSLog(#"String value=%#",self.animals);
}
// Now you can use countries to populate in picker.
You have to implement data source and delegates for the UIPickerView.
Seems, you are storing countries in DB, reading from DB you are assigning in self.animals. Thats kind of not matching things with variable names. And why are your parsing by ":"?
I have an SQLite database with 4 tables in it that I want to load into my Core Data database so I can implement core data. Can anyone help me with this?
I have the core data app ready, and I have the SQLite database ready. I need to copy the data from SQLite tables their 4 respective entities in core data in App delegate.
I'm currently using this code, how can I edit it so that instead of adding to array, it creates a new object in MOC?
-(NSMutableArray *) getCPTCodes
{
sqlite3 *database = CodesDatabase();
NSMutableArray *cptCodes = [[[NSMutableArray alloc] init] autorelease];
NSString *nsquery = [[NSString alloc] initWithFormat:#"SELECT code, short_description, medium_description FROM cpt_codes"];
const char *query = [nsquery UTF8String];
[nsquery release];
sqlite3_stmt *statement;
int prepareCode = (sqlite3_prepare_v2( database, query, -1, &statement, NULL));
if(prepareCode == SQLITE_OK) {
while (sqlite3_step(statement) == SQLITE_ROW)
{
CPTCodes *code = [[CPTCodes alloc] init];
code.code = [NSString stringWithCString:(char *)sqlite3_column_text(statement, 0) encoding:NSUTF8StringEncoding];
code.name = [NSString stringWithCString:(char *)sqlite3_column_text(statement, 1) encoding:NSUTF8StringEncoding];
code.description = [NSString stringWithCString:(char *)sqlite3_column_text(statement, 2) encoding:NSUTF8StringEncoding];
[cptCodes addObject:code];
// NSLog(#"Code: %# Short Description: %# Long Description: %#", hcpcsCode.code, hcpcsCode.name, hcpcsCode.description);
[code release];
}
sqlite3_finalize(statement);
}
return cptCodes;
}
Instead of loading that into an array, I need to add it as an object in core data, using this?
insertNewObjectForEntityForName:inManagedObjectContext:
It looks like you can create an entity and NSManagedObject subclass to take the place of the Code class in your existing code.
Your entity would look like:
CPTCode{
code:string
name:string
desc:string // "description" is a method of NSObject so you can't use it as a property name
}
... and then you could have Xcode generate a custom subclass, lets call it CPTCodeMO just for clarity.
Then you adapt your existing code:
CPTCodeMO *code;
while (sqlite3_step(statement) == SQLITE_ROW){
code=[NSEntityDescription insertNewObjectForEntityForName:#"CPTCode" inManagedObjectContext:self.managedObjectContext];
code.code = [NSString stringWithCString:(char *)sqlite3_column_text(statement, 0) encoding:NSUTF8StringEncoding];
code.name = [NSString stringWithCString:(char *)sqlite3_column_text(statement, 1) encoding:NSUTF8StringEncoding];
code.description = [NSString stringWithCString:(char *)sqlite3_column_text(statement, 2) encoding:NSUTF8StringEncoding];
}
Each pass through the loop would create a new CPTCodeMO instances and insert it in the managed object context. When you have created all the new instances you would just save the context:
[self.managedObjectContext save:&saveError];
... and your done.
There are nuances of course but this is the basic idea.
Do that once and you will have a Core Data SQLite store file with the existing data. You can then include that in the app bundle to ship with the app or do something else with it.
I usually create a small utility app project in Xcode just to do this kind of import.
i have multiple view controllers, registrationviewcontroller , contactsviewcontroller , addnewcontactsviewcontroller, etc.
i have the below code in registrationviewcontroller
DatabaseCRUD *database = [[DatabaseCRUD alloc] init];
NSMutableString *registrationquery=[NSMutableString stringWithFormat:#"insert into BARvalues values ('%#','%#','%#','%#','%#','%#')",[name text],[emailAddress text],[mobileNumber text],[password text],[country text],#"0"];
NSLog(#"registrationquery is %#",registrationquery);
[database updatenewregistration:registrationquery];
[database release];
DatabaseCRUD *database1 = [[DatabaseCRUD alloc] init];
NSMutableString *registrationquery1=[NSMutableString stringWithFormat:#"insert into contacts values ('1234','1234')"];
NSLog(#"registrationquery is %#",registrationquery1);
[database1 updatenewregistration:registrationquery1];
[database1 release];
DatabaseCRUD *database2 = [[DatabaseCRUD alloc] init];
NSMutableString *registrationquery2=[NSMutableString stringWithFormat:#"insert into contacts values ('abcd','abcd')"];
NSLog(#"registrationquery is %#",registrationquery2);
[database2 addcontacts:registrationquery2];
[database2 release];
the above code works fine . it inserts the values into the sqlite database. Here is the problem , i have the almost same code in a different viewcontroller(contacts, addcontacts) and the values dont get inserted into the sqlite database.Here is the code.
DatabaseCRUD *database = [[DatabaseCRUD alloc] init];
NSMutableString *registrationquery=[NSMutableString stringWithFormat:#"insert into contacts values ('raja','shekar')"];
NSLog(#"registrationquery is %#",registrationquery);
[database updatenewregistration:registrationquery];
[database release];
DatabaseCRUD *database1 = [[DatabaseCRUD alloc] init];
NSMutableString *registrationquery1=[NSMutableString stringWithFormat:#"insert into contacts values ('rajesh','neelam')"];
NSLog(#"registrationquery is %#",registrationquery1);
[database1 addcontacts:registrationquery1];
[database1 release];
i dont see any reason why the database object would insertin in one viewcontroller and doesnt in the other viewcontroller. Any help will be greatly appreciated. i have tried to figure where the error is all day long and i dont want to waste anymore time. any help please????
I put breakpoints at every method of database object to see if its inserting, there is no error at all . it seems to be passing through the sqlstatment, but no insertion happens. Here is the code of saving the values in databaseHere is the code for database insertion.
-(void) addcontacts :(NSString *)query
{
if(sqlite3_open([databasePath UTF8String], &database) == SQLITE_OK)
{
NSLog(#"query is %#",query);
sqlStatement=[query cStringUsingEncoding:NSASCIIStringEncoding];
if(sqlite3_prepare_v2(database, sqlStatement, -1, &compiledStatement, NULL) == SQLITE_OK)
{
while(sqlite3_step(compiledStatement) == SQLITE_ROW)
{
NSLog(#"new contact updated");
}
sqlite3_finalize(compiledStatement);
}
sqlite3_close(database);
}
}
This is somewhere between an answer and a comment - interpret as necessary.
I assume that the insert statements return some error code, correct? What are their values? This will send you a long ways towards your solution.
i have the following code which shows memory leak for object favorite near the statement with stringWithUTF8String.
i have declared favorites in the property
-(NSMutableArray *) readFavoritesFromDatabase
{
// Check if database is present
[self setDatabaseNameAndPath];
[self checkAndCreateDatabase];
// Setup the database object
sqlite3 *database;
//Initialize favorites array
if (favorites == nil)
{
[favorites release];
favorites = [[NSMutableArray alloc] init];
}
else
{
favorites = nil;
[favorites removeAllObjects];
}
// Open the database from the users file system
if(sqlite3_open([self.dataBasePath UTF8String], &database) == SQLITE_OK)
{
// Setup the SQL Statement and compile it for faster access
const char *sqlStatement = "select * from Favorites";
sqlite3_stmt *compiledStatement;
if(sqlite3_prepare_v2(database, sqlStatement, -1, &compiledStatement, NULL) == SQLITE_OK)
{
// Loop through the results and add them to the favorites array
while(sqlite3_step(compiledStatement) == SQLITE_ROW)
{
// Create Favorite object and add it to the Favorite array
Favorite *favorite = [[[Favorite alloc] init] autorelease];
favorite.cameraID = [NSString stringWithUTF8String:(const char*)sqlite3_column_text(compiledStatement, 0)];
favorite.cameraName = [NSString stringWithUTF8String:(const char*)sqlite3_column_text(compiledStatement, 1)];
favorite.cameraLink = [NSString stringWithUTF8String:(const char*)sqlite3_column_text(compiledStatement, 2)];
[self.favorites addObject:favorite];
//[favorite.cameraID release];
// [favorite.cameraName release];
// [favorite.cameraLink release];
}
// If favorite cameras exists in database, then sort the Favorites array
if([self.favorites count]>0)
{
NSSortDescriptor *favoritesNameSorter = [[NSSortDescriptor alloc] initWithKey:#"cameraName" ascending:YES];
[self.favorites sortUsingDescriptors:[NSArray arrayWithObject:favoritesNameSorter]];
[favoritesNameSorter release];
}
}
// Release the compiled statement from memory
sqlite3_finalize(compiledStatement);
}
// Close the database
if(database !=nil)
{
sqlite3_close(database);
return self.favorites;
}
else
{
return nil;
}
}
Please let me know how to solve this memory leak problem
Thanks in advance.
Use this safe method:
Favorite *tempFavorite = [[Favorite alloc] init];
self.favorite = tempFavorite;
[tempFavorite release];
Normaly, in your Favorite dealloc function, you should remove all objects and clean what necessary before calling the super dealloc function.
Using this way, you don't need to worry about if favorite is nil or not since objective-c allows calling methods for nil objects
Regards
Meir Assayag
Not sure about the stringWithUTF8String leak, but this is a problem:
favorites = nil;
[favorites removeAllObjects];
You leak what was in favorites and then tell a nil object to remove all objects -- it's nil, by definition it has none. Then later on you try to add objects to it; that won't work either.
I don't see any leak in your stringWithUTF8String, that code works well. However, look at the whole method, I found something can create memory problems like leaking or crashing. If you already declared a property for favorites, then you should use self.favorites here
//Initialize favorites array
if (favorites == nil)
{
[favorites release];
favorites = [[NSMutableArray alloc] init];
}
else
{
favorites = nil;
[favorites removeAllObjects];
}
becomes:
//Initialize favorites array
if (self.favorites == nil)
{
self.favorites = [[NSMutableArray alloc] init];
}
else
{
self.favorites = nil;
}
It will help you a lot of things in memory managements, like in your else condition, you set the variable to nil but not release it, and in the first condition, you release a nil object?