Bulk inserts into sqlite db on the iphone - iphone

I'm inserting a batch of 100 records, each containing a dictonary containing arbitrarily long HTML strings, and by god, it's slow. On the iphone, the runloop is blocking for several seconds during this transaction. Is my only recourse to use another thread? I'm already using several for acquiring data from HTTP servers, and the sqlite documentation explicitly discourages threading with the database, even though it's supposed to be thread-safe... Is there something I'm doing extremely wrong that if fixed, would drastically reduce the time it takes to complete the whole operation?
NSString* statement;
statement = #"BEGIN EXCLUSIVE TRANSACTION";
sqlite3_stmt *beginStatement;
if (sqlite3_prepare_v2(database, [statement UTF8String], -1, &beginStatement, NULL) != SQLITE_OK) {
printf("db error: %s\n", sqlite3_errmsg(database));
return;
}
if (sqlite3_step(beginStatement) != SQLITE_DONE) {
sqlite3_finalize(beginStatement);
printf("db error: %s\n", sqlite3_errmsg(database));
return;
}
NSTimeInterval timestampB = [[NSDate date] timeIntervalSince1970];
statement = #"INSERT OR REPLACE INTO item (hash, tag, owner, timestamp, dictionary) VALUES (?, ?, ?, ?, ?)";
sqlite3_stmt *compiledStatement;
if(sqlite3_prepare_v2(database, [statement UTF8String], -1, &compiledStatement, NULL) == SQLITE_OK)
{
for(int i = 0; i < [items count]; i++){
NSMutableDictionary* item = [items objectAtIndex:i];
NSString* tag = [item objectForKey:#"id"];
NSInteger hash = [[NSString stringWithFormat:#"%#%#", tag, ownerID] hash];
NSInteger timestamp = [[item objectForKey:#"updated"] intValue];
NSData *dictionary = [NSKeyedArchiver archivedDataWithRootObject:item];
sqlite3_bind_int( compiledStatement, 1, hash);
sqlite3_bind_text( compiledStatement, 2, [tag UTF8String], -1, SQLITE_TRANSIENT);
sqlite3_bind_text( compiledStatement, 3, [ownerID UTF8String], -1, SQLITE_TRANSIENT);
sqlite3_bind_int( compiledStatement, 4, timestamp);
sqlite3_bind_blob( compiledStatement, 5, [dictionary bytes], [dictionary length], SQLITE_TRANSIENT);
while(YES){
NSInteger result = sqlite3_step(compiledStatement);
if(result == SQLITE_DONE){
break;
}
else if(result != SQLITE_BUSY){
printf("db error: %s\n", sqlite3_errmsg(database));
break;
}
}
sqlite3_reset(compiledStatement);
}
timestampB = [[NSDate date] timeIntervalSince1970] - timestampB;
NSLog(#"Insert Time Taken: %f",timestampB);
// COMMIT
statement = #"COMMIT TRANSACTION";
sqlite3_stmt *commitStatement;
if (sqlite3_prepare_v2(database, [statement UTF8String], -1, &commitStatement, NULL) != SQLITE_OK) {
printf("db error: %s\n", sqlite3_errmsg(database));
}
if (sqlite3_step(commitStatement) != SQLITE_DONE) {
printf("db error: %s\n", sqlite3_errmsg(database));
}
sqlite3_finalize(beginStatement);
sqlite3_finalize(compiledStatement);
sqlite3_finalize(commitStatement);

The thing that you need to be aware of is that the SQLite documentation warns you away from accessing/writing to the database from multiple threads. As long as you access the database from a single thread, you'll be fine. It doesn't matter if that thread is your program's main thread or some other thread.
Keep in mind that compiled version of SQLite on the iPhone has its threading mode set to "multi-thread" which, according to the documentation, "disables mutexing on database connection and prepared statement objects. The application is responsible for serializing access to database connections and prepared statements but other mutexes are enabled so that SQLite will be safe to use in a multi-threaded environment as long as no two threads attempt to use the same database connection at the same time." So, if you do decide to put this transaction on another thread, be careful of what else you try to do with the database.
That being said, I'd first follow Yonel's advice and switch to "BEGIN" AND "COMMIT". If that doesn't help, move the transaction to another thread. Working with "blobs" can be pretty slow, from what I've heard.

Did you try the same as your code but with "BEGIN" and "COMMIT" instead of "BEGIN EXCLUSIVE TRANSACTION" and "COMMIT TRANSACTION" ?
I'm simply using BEGIN and COMMIT and it's pretty much faster than committing for each transaction so I guess it's working with those keywords.
http://www.sqlite.org/lang_transaction.html

I see a lot of cases where developers new to the iPhone believe the code is slow when it is simply a case of asking lightweight hardware to do to much processing. Processing several hundred (thousands?) of "arbitrarily long HTML strings" might to heavy a task for the iPhone to carry out in a timely fashion.
Remember that the iPhone isn't a very powerful piece of hardware. It pulls off all the nifty graphics with dedicated hardware whose computational power you can't access for other task. Even if you optimize the code it maybe way slower than you would intuitively expect based on your experience with full fledged laptops and desktops.
Instead of guessing where the bottleneck is, I suggest that you profile the code with Instruments (or even just using NSLog with timestamps) to see exactly where the code is spending most of its time.

A better approach to avoid blocking problems is to use an asynchronous callbacks.
Try using Enorm EGO sqlite wrapper https://github.com/jdp-global/egodatabase
Have a look at my Readme section for EGODatabaseRequest - asynchronous requests /inserts to db.
2) Add the requestDidSucceed /requestDidFail callback methods.
-(void)requestDidSucceed:(EGODatabaseRequest*)request withResult:(EGODatabaseResult*)result
idx++
if ([items count]<idx) [self insertRow];
}
-(void)requestDidFail:(EGODatabaseRequest*)request withError:(NSError*)error{
NSLog(#"WARNING requestDidFail");
}
-(void)insertRow{
NSMutableDictionary* item = [items objectAtIndex:idx];
NSInteger hash = [[NSString stringWithFormat:#"%#%#", tag, ownerID] hash];
NSInteger timestamp = [[item objectForKey:#"updated"] intValue];
NSData *dictionary = [NSKeyedArchiver archivedDataWithRootObject:item];
NSString *qry = [NSString stringWithFormat:#"INSERT OR REPLACE INTO item (hash, tag, owner, timestamp, dictionary) VALUES (%#, %#, %#, %#, %#);",NUMBER(hash),[tag UTF8String],[ownerID UTF8String],NUMBER(timestamp),dictionary];
// be sure to use NSNumbers not NSIntegers
EGODatabaseRequest* request = [[EGODatabaseRequest alloc] initWithQuery:qry parameters:nil];
request.delegate = self;
request.database = appDelegate.database;
request.requestKind = EGODatabaseUpdateRequest; // use update not select
[request fire];
[request release];
}

Related

Do the changes made in sql reflected in core data?

I have sql as well as core data being used in my application.Now, if i make some changes suppose, update the sql db using sql queries i have those update made.If i make fetch request and print the array i see the same old value unless i quit the app and restart it.I am not able to understand this behavior.I wanted to know that if changes are made in sql db then do those changes reflect in core data table? If not, then do i have to save the updated values in coredata well and will it lead to duplicate or redundant data?
This is the code being used to update values:
I have an array containing values:
- (void) updateGeneraldata3 :(NSArray*) marrDataUpdate
{
sqlite3 *database;
sqlite3_stmt *update_statement = nil;
if(sqlite3_open([strDatabasePath UTF8String], &database) == SQLITE_OK)
{
NSString *strMQueryupdate = [NSString stringWithFormat:#"UPDATE zform_general_data1 SET zChk_Mild='%#', zChk_CTV='%#', zChk_DOA='%#', zChk_ALS1='%#' where zautonumber='%#'",[marrDataUpdate objectAtIndex:0],[marrDataUpdate objectAtIndex:1],[marrDataUpdate objectAtIndex:2],[marrDataUpdate objectAtIndex:3],autoNumberTextField.text];
const char *sql = [strMQueryupdate UTF8String];
if (sqlite3_prepare_v2(database, sql, -1, &update_statement, NULL) != SQLITE_OK) {
NSLog(#"update fails");
}
else
{
sqlite3_bind_text(update_statement, 1, [[marrDataUpdate objectAtIndex:0] UTF8String], -1, SQLITE_TRANSIENT);
sqlite3_bind_text(update_statement, 2, [[marrDataUpdate objectAtIndex:1] UTF8String], -1, SQLITE_TRANSIENT);
sqlite3_bind_text(update_statement, 3, [[marrDataUpdate objectAtIndex:2] UTF8String], -1, SQLITE_TRANSIENT);
sqlite3_bind_text(update_statement, 4, [[marrDataUpdate objectAtIndex:3] UTF8String], -1, SQLITE_TRANSIENT);
int success = sqlite3_step(update_statement);
sqlite3_reset(update_statement);
if (success == SQLITE_ERROR){}
else {}
}
sqlite3_finalize(update_statement);
}
sqlite3_close(database);
}
And after update there is a background function which is executed after every 60 seconds of time .Now if i change the value of these fields on the view using the respective text fields, it updates the value in db using the above code which is also being executed after a certain interval of time :
-(void) functServerPostingData
{
#try
{
#autoreleasepool {
generalDataObject = [[GeneralData alloc] init];
generalData1_Array=[[NSArray alloc]init];
[self checkUsedAutoNumber];
NSError *error;
NSManagedObjectContext *mocGeneral1 = [self managedObjectContext];
NSEntityDescription *entityDescriptionGeneral1 = [NSEntityDescription
entityForName:#"Form_General_Data1" inManagedObjectContext:mocGeneral1];
NSFetchRequest *requestGeneral1;
requestGeneral1 = [[NSFetchRequest alloc] init];
[requestGeneral1 setEntity:entityDescriptionGeneral1];
generalData1_Array=[mocGeneral1 executeFetchRequest:requestGeneral1 error:&error];
NSLog(#"generalData1_Array :%#",generalData1_Array);
if(!generalData1_Array)
{
NSLog(#"Error fetching Login: %#", [error localizedDescription]);
abort();
}
for(int i = 0; i< [generalData1_Array count]; i++) {
for (int j=0;j<[[strAutoNumber componentsSeparatedByString:#","] count] ; j++)
{
if([[[generalData1_Array objectAtIndex:i] Autonumber ] isEqualToString: [[strAutoNumber componentsSeparatedByString:#","]objectAtIndex:j]])
{
generalDataObject.AutoNumber=[NSString stringWithFormat:#"%#",[[generalData1_Array objectAtIndex:i] Autonumber ]];
generalDataObject.Chk_Mild =[[generalData1_Array objectAtIndex:i]Chk_Mild];
generalDataObject.Chk_CTV=[[generalData1_Array objectAtIndex:i]Chk_CTV];
generalDataObject.Chk_PAU=[[generalData1_Array objectAtIndex:i]Chk_PAU];
generalDataObject.Transient=[[generalData1_Array objectAtIndex:i]Transient];
generalDataObject.Chk_ALS1= [[generalData1_Array objectAtIndex:i]Chk_ALS1];
[generalDataObject startPostServerData];
}
}
}
if (generalDataObject.checkRequest==TRUE)
{
[appDelegate CheckInternetConnection];
if(appDelegate.internetWorking==-1)
{
}
else {
serverLinkToHit = [NSString stringWithFormat:#"%#/ReceiveData.aspx?m=InsertData&t=GeneralData",[serverLinkSettings getServerLink ]];
[generalDataObject readServerData:serverLinkToHit postServerData:generalDataObject.finalDataString];
}
}
generalDataObject = nil;
requestGeneral1=nil;
requestGeneral2=nil;
}
}
#catch (NSException *exception)
{
[ExceptionLog LogEntry:exception :#"Error while entering data functServerPostingData"];
}
}
I have printed the array which always shows the previous old value irrespective of what i have entered in text field and saving in db.However, at the same place i print the values of sql db they are updated.Shall i use sql select to select value put it in an array and then post?
No. That's not how Core Data works. The format of the database is not documented and you're not supposed to play around with it.
I think the real question here is: why do you think you need to use SQL? Looks like you're just updating a few records. This is pretty straight-forward using "native" Core Data.

UITableViewController not always consistent with actual SQLite database until after restarting app

In my "viewWillAppear" callback I attempt to populate a UITableViewController with data from SQLite database for the user to see.
However, what I noticed was if I switch to another tab, commit a new row of data into SQLite and switch back to the UITableViewController it does not update with the new row I just added to the database. I have to quit out of the app completely and navigate back to the UITableViewController in order to see the new row reflected on the table view.
How do I get around this problem (i.e. how do I force always showing the very latest information in SQLite on the UITableViewController after switching back and forth a bunch of times?)
Would appreciate all / any advice.
Here is the code:
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
NSLog(#"viewwillappear");
//[[self tableView] reloadData];
int rc=-1;
if (databasePath == nil) {
NSLog(#"database path is NIL. Trying to set it");
databaseName = #"mymemories.sqlite";
NSArray *documentPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDir = [documentPaths objectAtIndex:0];
databasePath = [documentsDir stringByAppendingPathComponent:databaseName];
return;
}
rc = sqlite3_open([databasePath UTF8String], &database);
if(rc == SQLITE_OK) {
memoriesArray = [[NSMutableArray alloc]init ];
sqlite3_stmt *statement = nil;
NSString *fullQuery = #"SELECT * FROM memories";
const char *sql = [fullQuery UTF8String];
if(sqlite3_prepare_v2(database, sql, -1, &statement, NULL)!=SQLITE_OK)
NSAssert1(0, #"Error preparing statement '%s'", sqlite3_errmsg(database));
else
{
while(sqlite3_step(statement) == SQLITE_ROW)
{
NSString *place= [NSString stringWithUTF8String:(const char*)sqlite3_column_text(statement, 4)];
//[User setName:[NSString stringWithUTF8String:(const char*)sqlite3_column_text(statement, 1)]];
//[User setAge:[NSString stringWithUTF8String:(const char*)sqlite3_column_text(statement, 2)]];
[memoriesArray addObject:place];
//[currentUser release];
}
}
sqlite3_finalize(statement);
sqlite3_close(database);
}
}
Also in case this is relevant, here is the code that commits to the SQLite database:
NSData * blob = [NSData dataWithContentsOfURL:recordedTmpFile];
int rc=-1;
rc = sqlite3_open([databasePath UTF8String], &database);
if(rc == SQLITE_OK) {
sqlite3_exec(database, "BEGIN", 0, 0, 0);
NSLog(#"Connected To: %#",databasePath);
sqlite3_stmt *updStmt =nil;
const char *sql = "INSERT INTO memories (data,place) VALUES (?,?);";
rc = sqlite3_prepare_v2(database, sql, -1, &updStmt, NULL);
if(rc!= SQLITE_OK)
{
NSLog(#"Error while creating update statement:%#", sqlite3_errmsg(database));
}
sqlite3_bind_text( updStmt, 2, [[tags text] UTF8String], -1, SQLITE_TRANSIENT);
rc = sqlite3_bind_blob(updStmt, 1, [blob bytes], [blob length] , SQLITE_BLOB);
if((rc = sqlite3_step(updStmt)) != SQLITE_DONE)
{
NSLog(#"Error while updating: %#", sqlite3_errmsg(database));
sqlite3_reset(updStmt);
}
sqlite3_exec(database, "COMMIT", 0, 0, 0);
//rc = sqlite3_reset(updStmt);
sqlite3_close(database);
}
As an extension of an explanation of Darren's answer.
First off his answer is correct.
Secondly you need to use an NSMutableArray to ensure consistency, this is where you are going wrong by not updating it as you should
The steps you should be taking to ensure consistency are the following:
Loading Data into Table
In viewDidLoad, call your SQL statement and load it into your array
in viewWillAppear ensure that your array contains data, if not display a notice that no results were returned
Saving Data into Database
Update the change to the array (or datasource)
Update the Database with the updated datasource to ensure consistency
Update the table with one of the 4 UITableView reloading methods
Using the NSMArray to ensure consistency between updates and app loads is fairly common practise has be recommended to me in the past by fellow co workers with decades of experience.
Note:
You will need to synchronise the datasource to ensure that 1 thread is accessing it at any 1 time otherwise you will get a crash.
Assuming you read your SQL data into an array, then use this array to build the UITableView, when you add a record to your SQL database, you either need to also add it to the array used to build the table, or re-read the data from the database into the array.

Sqlite crash app

i am using this code to insert names to table,i have a problem that after 150 +/- names the app crash with this log:
Received memory warning. Level=1
Received memory warning. Level=2
this is the code,did i done something wrong?
if (sqlite3_open([dataPath UTF8String], &database) == SQLITE_OK) {
for (int i = 0 ; i < count; i++) {
sqlite3_stmt *insertStmt = nil;
NSString *name = [song valueForProperty:MPMediaItemPropertyTitle];
if(insertStmt == nil)
{
NSString *statement = [NSString stringWithFormat:#"INSERT INTO Songs (name) VALUES (?)"];
const char *insertSql = [statement UTF8String];
if(sqlite3_prepare_v2(database, insertSql, -1, &insertStmt, NULL) != SQLITE_OK){
NSLog(#"Error while creating insert statement.");
insertStmt = nil;
continue;
}
sqlite3_bind_text(insertStmt, 1, [name UTF8String], -1, SQLITE_TRANSIENT);
if(SQLITE_DONE != sqlite3_step(insertStmt)){
NSLog(#"Error while inserting data.");
insertStmt = nil;
continue;
}
else{}
sqlite3_reset(insertStmt);
insertStmt = nil;
}
[delegate IPodLibraryFinishEntity:self];
}
sqlite3_close(database);
}
Use instruments to check for memory loss due to retained but not leaked memory. The latter is unused memory that is still pointed to. Use Heapshot in the Allocations instrument on Instruments.
For HowTo use Heapshot to find memory creap, see: bbum blog
Basically there method is to run Instruments allocate tool, take a heapshot, run an intuition of your code and another heapshot repeating 3 or 4 times. This will indicate memory that is allocated and not released during the iterations.
To figure out the results disclose to see the individual allocations.
If you need to see where retains, releases and autoreleases occur for an object use instruments:
Run in instruments, in Allocations set "Record reference counts" on on (you have to stop recording to set the option). Cause the picker to run, stop recording, search for there ivar (datePickerView), drill down and you will be able to see where all retains, releases and autoreleases occurred.
Your code is not optimal. You should place all prepare methods before the loop.
if (sqlite3_open([dataPath UTF8String], &database) == SQLITE_OK) {
sqlite3_stmt *insertStmt = nil;
NSString *name = [song valueForProperty:MPMediaItemPropertyTitle];
if(insertStmt == nil) {
NSString *statement = [NSString stringWithFormat:#"INSERT INTO Songs (name) VALUES (?)"];
const char *insertSql = [statement UTF8String];
if(sqlite3_prepare_v2(database, insertSql, -1, &insertStmt, NULL) != SQLITE_OK){
NSLog(#"Error while creating insert statement.");
insertStmt = nil;
return;
}
}
for (int i = 0 ; i < count; i++) {
sqlite3_bind_text(insertStmt, 1, [name UTF8String], -1, SQLITE_TRANSIENT);
if(SQLITE_DONE != sqlite3_step(insertStmt)){
NSLog(#"Error while inserting data.");
continue;
}
else{}
sqlite3_clear_bindings(insertStmt); //release bindings
sqlite3_reset(insertStmt);
[delegate IPodLibraryFinishEntity:self];
}
sqlite3_close(database);
}
Before starting each Insert statement use sqlite3_open and after execution of query put sqlite3_close statement. So that it wont make database object busy anymore after each Insert query execution.

Bulk Insert to Sqlite3 causing Db Error: Library routine called out of sequence

I need to load some configuration data after app updates for my iphone app. I bundle in a file with a bunch of SQL statements (800+) to run on first launch. It looks like I may be painted in a corner now. If I run it on the main thread at startup, it take so long to run that the app crashes due to the startup taking too long. If I run it on a separate thread, I am getting database contention errors (Library routine called out of sequence). I think this is because the app is continuing to load and read the DB on the main thread.
Here is the method that receives the data form the CSV file and then loops through and writes to the DB.
Any ideas about how to either make this run faster on startup or run successfully without contention on a low priority background thread?
+ (void) updateDB:(NSString *)data {
NSArray *lineArray = [data componentsSeparatedByString:#"\n"];
if (sqlite3_open([[BIUtility getDBPath] UTF8String], &database) != SQLITE_OK) {
sqlite3_close(database);
NSLog(#"Failed to opendatabase in updateDB");
}
char *errorMsg;
for(int k = 0; k < [lineArray count]; k++){
NSString *loadSQLi = [lineArray objectAtIndex:k];
if (sqlite3_exec(database, [loadSQLi UTF8String], NULL, NULL, &errorMsg) != SQLITE_OK) {
NSLog(#"DB Error. '%s'", sqlite3_errmsg(database));
}
}
if(database) sqlite3_close(database);
}
You can make this faster by doing all inserts in a single transaction instead of doing the inserts in separate transactions as it happens by default in that code.
+ (void) updateDB:(NSString *)data {
NSArray *lineArray = [data componentsSeparatedByString:#"\n"];
if (sqlite3_open([[BIUtility getDBPath] UTF8String], &database) != SQLITE_OK) {
sqlite3_close(database);
NSLog(#"Failed to opendatabase in updateDB");
}
char *errorMsg;
execQuery(#"Begin Transaction");
for(int k = 0; k < [lineArray count]; k++){
NSString *loadSQLi = [lineArray objectAtIndex:k];
execQuery(loadSQLi);
}
execQuery(#"Commit");
if(database) sqlite3_close(database);
}
+ (void) execQuery:(NSString *)query {
char *errorMsg;
if (sqlite3_exec(database, [query UTF8String], NULL, NULL, &errorMsg) != SQLITE_OK) {
NSLog(#"DB Error. '%s'", sqlite3_errmsg(database));
}
}

How to bind text value to sqlite3 database in iphone

I am developing a app for a movie in which I need to fetch data from the database considering some constraints. It works perfectly on the first occasion, but when I try to fetch data 2nd time it throws a runtime exception( the app crashes).I have to bind 3 placeholders. 2 are text and 1 is integer type. Here's the code which I am using to fetch data from the database.
-(void) Data2
{
databaseName = #"Cinema1.sqlite";
NSArray *documentPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,NSUserDomainMask, YES);
NSString *documentsDir = [documentPaths objectAtIndex:0];
databasePath =[documentsDir stringByAppendingPathComponent:databaseName];
[self checkAndCreateDatabase];
sqlite3 *database;
if (sqlite3_open([databasePath UTF8String], &database) == SQLITE_OK)
{
if(detailStmt == nil)
{
const char *sql = "Select PVR,Fame,Cinemax,Big from listing where UPPER(State) = UPPER(?) and UPPER(City) = UPPER(?) and ZIP = ?";
if(sqlite3_prepare_v2(database, sql, -1, &detailStmt, NULL) == SQLITE_OK)
{
NSLog(#"Hiiiiiii");
sqlite3_bind_text(detailStmt, 1, [t1 UTF8String], -1, SQLITE_TRANSIENT);
sqlite3_bind_text(detailStmt, 2, [t2 UTF8String], -2, SQLITE_TRANSIENT);
sqlite3_bind_int(detailStmt, 3, t3);
if(SQLITE_DONE != sqlite3_step(detailStmt))
{
NSLog(#"Helllloooooo");
NSString *pvr= [NSString stringWithUTF8String:(char *)sqlite3_column_text(detailStmt, 0)];
NSString *fame= [NSString stringWithUTF8String:(char *)sqlite3_column_text(detailStmt, 1)];;
NSString *cinemax = [NSString stringWithUTF8String:(char *)sqlite3_column_text(detailStmt, 2)];
NSString *big= [NSString stringWithUTF8String:(char *)sqlite3_column_text(detailStmt, 3)];
pvr1 = pvr;
fame1 = fame;
cinemax1 = cinemax;
big1 = big;
NSLog(#"PVR %# Fame %# Cinemax %# Big %#",pvr1,fame1,cinemax1,big1);
}
}
sqlite3_finalize(detailStmt);
}
}
sqlite3_close(database);}
Can anyone help me with this.
You are using the sqlite3_prepare_v2() and sqlite3_finalize() wrong. You only prepare a statement once and then you can bind values to it as much as you'd like. When you're done you call sqlite3_reset(). When you're completely done with this statement and won't ever use it again (i.e. you quit the app), then you use finalize.
Your app crashes because you finalize the statement, but the pointer detailStmt still points to the position where your statement once was, and tries to access that region of the memory.
See also here: http://www.sqlite.org/c3ref/prepare.html
As Toby points out, the variables pvr, fame, cinemax, and big (and the reassigned pvr1, fame1, cinemax1, and big1) are autoreleased when returned from -stringWithUTF8String:, but you never retain them. Any access to these variables after this point will cause a crash.
You say that you are seeing a thrown exception. It might be helpful to know what that exception is, by examining the console output. Also, you can enable a breakpoint on objc_exception_throw in libobjc.A.dylib, then debug with breakpoints on, to find the exact line at which this exception occurs.
In my limited Iphone experience when something runs once and then crashes on the next iteration, generally you are releasing memory you shouldnt be or not releasing memory you should be. Try looking at your memory allocations for problems.