I am using NSTimer in Objective-C to run a background process periodically (1 min interval) which checks for any unsaved data and submits to database through a web service. This process runs fine without blocking the main application’s flow until one particular scenario when the web server was re-started.
When the web server was coming up the background process was trying to connect to the server and just hung up there until the server was fully started and reacheable. When it got hung up it also blocked the main applications flow and freezed the entire screen. When the webserver was fully started the hung up process resumed and the main application also resumed working.
Can anyone please let me know why the main application was intermittently freezed when a background process was hung up.
Following code start the timer
- (void) startSync{
syncTimer = [NSTimer scheduledTimerWithTimeInterval:60
target:self
selector:@selector(syncExtSys:)
userInfo:nil
repeats:YES];
}
Following method is called every 60 seconds and saveAgendaToDb method call calls the web service:
- (void) syncExtSys:(NSTimer *) theTimer{
UIStoryboard* storyboard = [UIStoryboard storyboardWithName:@"MainStoryboard" bundle:[NSBundle mainBundle]];
AgendaItemViewController *agendaItemViewController = [storyboard instantiateViewControllerWithIdentifier:@"AgendaItemViewController"];
RollCallViewController *rollCallViewController = [storyboard instantiateViewControllerWithIdentifier:@"RollCallViewController"];
for(int i=0;i<agenda.count;i++){
unsavedAgenda = [agenda objectAtIndex:i];
if(unsavedAgenda !=nil && [unsavedAgenda.dirtyFlag isEqualToString:@"Y"] && [self checkNetworkStatus]){
NSLog(@"agenda out of sync = %@ %@",unsavedAgenda.measureType,unsavedAgenda.measureNum);
[agendaItemViewController saveAgendaToDb:[agendaItemViewController createDictForAgenda:unsavedAgenda]:unsavedAgenda];
}
}
for(int i=0;i<_rollCall.count;i++){
Member *member = [_rollCall objectAtIndex:i];
if(member !=nil && [member.dirtyFlag isEqualToString:@"Y"] && [self checkNetworkStatus]){
[rollCallViewController saveRollCall:[rollCallViewController createRollCall]];
break;
}
}
[self calcDirtyFlag];
}
Code calling the web service
-(void) saveAgendaToDb:(NSMutableDictionary*) agendaDictionary:(AgendaItem*) agenda{
NSError *error;
CommVotesAppDelegate *delegate = (CommVotesAppDelegate *)[[UIApplication sharedApplication] delegate];
@try{
NSString *postUrlString = [NSString stringWithFormat:@"%@%@:%@%@%@/%@",@"http://",delegate.prop.serverName,
delegate.prop.port,delegate.prop.urlPattern,delegate.prop.saveAgendaMethod,delegate.prop.objectType];
NSURL *postUrl = [NSURL URLWithString:postUrlString];
NSMutableURLRequest *urlRequestPost = [[NSMutableURLRequest alloc] initWithURL:postUrl];
NSData *postData = [NSJSONSerialization dataWithJSONObject:agendaDictionary options:0 error:&error];
[urlRequestPost setHTTPMethod:@"POST"];
[urlRequestPost setValue:@"text/plain" forHTTPHeaderField:@"Accept"];
[urlRequestPost setValue:@"application/json" forHTTPHeaderField:@"Content-Type"];
[urlRequestPost setHTTPBody: postData];
// NSLog(@"After calling webservices");
NSData *returnData = [ NSURLConnection sendSynchronousRequest: urlRequestPost returningResponse: nil error: &error ];
NSString *returnString = [[NSString alloc] initWithData:returnData encoding: NSUTF8StringEncoding];
NSLog(@"Response after calling save agenda web service = %@",returnString);
[delegate dirtyFlagCalc:returnString :[error localizedDescription]:agenda];
}@catch(NSException *nserror){
NSLog(@"Error while sending agenda through webservices = %@",nserror.debugDescription);
}@finally {
NSLog(@"Inside finally block of agenda saving ");
[delegate saveDataToDisk];
}
}
The timer fires on the main thread. If the syncExtSys: method does not complete promptly it will hang the main thread.
Your situation is that you want to do something that may potentially take awhile but you want the results to be displayed in the UI on the main thread. Some good ways to handle this is to do one of the following with Grand Central Dispatch: