Search code examples
objective-cprofileruistoryboardsegue

iOS segue too slow


I started working with iOS. And using Storyboard to navigate between the view controllers. First screen is an uiviewcontroller(login screen) and second one is an uiviewcontroller with a table view. I'm sending some web requests in the first viewcontroller and then performing segue to the next viewcontroller. There is about 9 seconds delay to load the second view controller after getting the response. I tried checking the cause using Timer profiler and didnt find anything in my obj-c code.

If anyone can help me here is the link to my trace file.

My Log:

2014-05-08 10:12:12.601 Consumer[6713:4207] One 2014-05-08 10:12:12.602 Consumer[6713:4207] Three 2014-05-08 10:12:12.605 Consumer[6713:4207] Four 2014-05-08 10:12:12.606 Consumer[6713:4207] Two 2014-05-08 10:12:21.394 Consumer[6713:60b] numberOfSectionsInTableView 2014-05-08 10:12:21.395 Consumer[6713:60b] titleForHeaderInSection 2014-05-08 10:12:21.395 Consumer[6713:60b] numberOfRowsInSection 2014-05-08 10:12:21.395 Consumer[6713:60b] titleForHeaderInSection 2014-05-08 10:12:21.395 Consumer[6713:60b] numberOfRowsInSection 2014-05-08 10:12:21.396 Consumer[6713:60b] titleForHeaderInSection 2014-05-08 10:12:21.396 Consumer[6713:60b] numberOfRowsInSection 2014-05-08 10:12:21.396 Consumer[6713:60b] cellForRowAtIndexPath 2014-05-08 10:12:21.399 Consumer[6713:60b] returning cell 2014-05-08 10:12:21.399 Consumer[6713:60b] cellForRowAtIndexPath 2014-05-08 10:12:21.400 Consumer[6713:60b] returning cell 2014-05-08 10:12:21.401 Consumer[6713:60b] cellForRowAtIndexPath 2014-05-08 10:12:21.402 Consumer[6713:60b] returning cell 2014-05-08 10:12:21.402 Consumer[6713:60b] titleForHeaderInSection

My LoginViewController:

-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender{
if([segue.identifier isEqualToString:@"login_success_new"]){
    TableViewController *controller = (TableViewController *)segue.destinationViewController;
    controller.municipalitiesArray = municipalitiesArray;
    controller.muni_metergroup_dict = muni_metergroup_dict;
    NSLog(@"Three");
}
}

-(void) sendPostRequest:(NSURL *)requestURL requestParams:(NSDictionary *)params requestType:(NSInteger)type{
NSError *error;
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:requestURL];
[request addValue:@"application/json" forHTTPHeaderField:@"Content-Type"];
//[request addValue:@"application/json" forHTTPHeaderField:@"Accept"];
[request setHTTPMethod:@"POST"];

NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration defaultSessionConfiguration];
NSURLSession *session = [NSURLSession sessionWithConfiguration:configuration delegate:self delegateQueue:nil];

NSData *postData = [NSJSONSerialization dataWithJSONObject:params options:0 error:&error];
[request setHTTPBody:postData];


NSURLSessionDataTask *postDataTask = [session dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {

    NSDictionary *json = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil];
    NSLog(@"response = %@",json);


    if(json != nil){
        switch (type) {
            case 1:
            {

                for (NSObject *object in [json objectForKey:@"municipalities"]){
                    MunicipalityModel *muniModel = [[MunicipalityModel alloc] init];
                    [muniModel setModelValues:object];
                    [municipalitiesArray addObject:muniModel];
                }
                //municipalitiesArray = [json objectForKey:@"municipalities"];
                //MunicipalitiesModel *muniModel = [[MunicipalitiesModel alloc] init];
                //[muniModel setModelValues:municipalitiesArray[0]];
                userEmail = @"admin@example.com";
                tokenValue = [json objectForKey:@"token"];
                NSString *stringUrl = [NSString stringWithFormat:@"http://%@.quality.sentry-link.com/api/v1/meter_groups.json",[municipalitiesArray[count] muniSubdomain]];
                NSURL *url = [NSURL URLWithString:stringUrl];
                [self sendGetRequest:url requestParams:nil requestType:2];

                break;
            }
            default:
                break;
        }
        //[self performSegueWithIdentifier:@"login_success" sender:self];
    }
}];

[postDataTask resume];

}

-(void) sendGetRequest:(NSURL *)requestURL requestParams:(NSDictionary *)params requestType:(NSInteger)type{

NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:requestURL];
[request addValue:@"application/json" forHTTPHeaderField:@"Content-Type"];
[request setHTTPMethod:@"GET"];

NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration defaultSessionConfiguration];
NSURLSession *session = [NSURLSession sessionWithConfiguration:configuration delegate:self delegateQueue:nil];

NSLog(@"Request URL =====> %@",requestURL);

NSURLSessionDataTask *getDataTask = [session dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {

    NSDictionary *json = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil];
    NSLog(@"response = %@",json);


    if(json != nil){
        switch (type) {
            case 1:
            {
                break;
            }
            case 2:
            {
                 NSMutableArray *meterGroupsArray = meterGroupsArray = [[NSMutableArray alloc] init];

                for (NSObject *object in [json objectForKey:@"meter_groups"]){
                    MeterGroupModel *meterGroupModel = [[MeterGroupModel alloc] init];
                    [meterGroupModel setModelValues:object];
                    [meterGroupsArray addObject:meterGroupModel];
                }
                count++;
                [muni_metergroup_dict setValue:meterGroupsArray forKey:[municipalitiesArray[count-1] muniSubdomain]];
                if (count < [municipalitiesArray count]) {
                    NSString *stringUrl = [NSString stringWithFormat:@"http://%@.quality.sentry-link.com/api/v1/meter_groups.json",[municipalitiesArray[count] muniSubdomain]];
                    NSURL *url = [NSURL URLWithString:stringUrl];
                    [self sendGetRequest:url requestParams:nil requestType:2];
                }
                break;
            }

            default:
                break;
        }
        if(count ==[municipalitiesArray count]){
            NSLog(@"One");
            [self performSegueWithIdentifier:@"login_success_new" sender:self];
            NSLog(@"Two");
        }

    }
}];

[getDataTask resume];
}

TableviewController:

- (void)viewDidLoad
{
[super viewDidLoad];
//tableView.contentInset = UIEdgeInsetsMake(20, 0, 0, 0);
NSLog(@"Four");
}

- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}

- (IBAction)buttonClicked:(id)sender{
NSLog(@"button Clicked");
}

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
// Return the number of sections.
NSLog(@"numberOfSectionsInTableView");
return [_municipalitiesArray count];
}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
// Return the number of rows in the section.
//Number of rows it should expect should be based on the section

NSString *sectionSubdomain = [_municipalitiesArray[section] muniSubdomain];
NSArray *array = [_muni_metergroup_dict objectForKey:sectionSubdomain];
NSLog(@"numberOfRowsInSection");
return [array count];
}

- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section {
NSLog(@"titleForHeaderInSection");
return [self.municipalitiesArray [section] muniName];
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSLog(@"cellForRowAtIndexPath");
long row = [indexPath row];
long section = [indexPath section];
NSArray *meterGroupArray = [_muni_metergroup_dict valueForKey:[_municipalitiesArray[section] muniSubdomain]];
static NSString *CellIdentifier = @"tableCell";

TableViewCell *cell = [tableView
                          dequeueReusableCellWithIdentifier:CellIdentifier
                          forIndexPath:indexPath];

// Configure the cell...

[cell.lblMeterGroupName setText:[meterGroupArray[row] groupName]];
[cell.lblMeterGroupId setText:[meterGroupArray[row] groupId]];
[cell.lblLatitude setText:[meterGroupArray[row] latitude]];
[cell.lblLongitude setText:[meterGroupArray[row] longitude]];



NSLog(@"returning cell");
return cell;
}

Thanks in advance!


Solution

  • The problem, clearly displayed in your log, is that One, Two, Three, and Four are being performed on a background thread. This means, for example, that you are saying:

    [self performSegueWithIdentifier:@"login_success_new" sender:self];
    

    ...on a background thread. That is so wrong. You must NEVER say anything to or about the interface on a background thread. You need to get your threading straightened out. (The fact that One, Two, Three, and Four do not happen in order is also a clue that you may have set this up incoherently.)

    EDIT: In a comment, you say that you now used dispatch_async(dispatch_get_main_queue()..., thus correctly moving the call to performSegueWithIdentifier: onto the main thread. Excellent! Your logging should now show that all calls connected with the interface (including especially "Three" in prepareForSegue:) are now on the main thread.

    However, you still need to be careful. Basically everything in both completion: handlers happens on a background thread! You are even calling [self sendGetRequest:...] on a background thread! And you are apparently talking to instance variables of self on this a background thread. All of that is dangerous. What I would do is step out to the main thread right at the start of each completion handler, so that even if the completion handler is called on a background thread, everything I then do happens on the main thread.