Search code examples
iosiphoneobjective-cipadnsthread

Tableview scrolling crashes app


I am developing iPhone app in which i am downloading image from server in background,

Here is view of my application,

when i click on Button 1 i am fetching 5 data from server also images, after fetching data when user scrolls up i am fetching new 5 data from server again when user scrolls up i am fetching new 5 data from server and so on.

while fetching data for Button 1 if i click on Button 2 am cancelling my previous thread of Button 1 and i am fetching new 5 data for Button 2 and on scrolling it again fetching new 5 data Same as Button 1

but after some time while reloading a tableview my app gets crashes and shows: Terminating app due to uncaught exception 'NSRangeException', reason: ' -[__NSArrayM objectAtIndex:]: index 28 beyond bounds [0 .. 4]

Here is my code snippet:

- (void)viewDidLoad
{
  dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{

        [self fetchDataForButton1]; 

        dispatch_async(dispatch_get_main_queue(), ^(void) {


                [tableView reloadData];


        });
    });

}


- (UITableViewCell*)tableView:(UITableView*)tableView cellForRowAtIndexPath:(NSIndexPath*)indexPath
{
 if(it is last row then fetch new 5 data)
 {
   dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{

                        //Code runs on background thread
                        [self LoadMoreData];

                        dispatch_async(dispatch_get_main_queue(), ^(void) {

                            //Code here is run on the main thread
                            [_tblList reloadData];

                        });
                    });


      }else{
              dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{

                                [self beginBackgroundFetchTask];

                               [self downloadImage_3:indexPath];

                                [self endBackgroundFetchTask];
                            });
      } 

}


-(void)downloadImage_3:(NSIndexPath *)path{


        UIImage *img = [[UIImage alloc] initWithData:[NSData dataWithContentsOfURL:[NSURL URLWithString:ImagePath]]];

        if (img) {
            [dicImages_msg setObject:img forKey:[[msg_array objectAtIndex:path.row] valueForKey:@"Merchant_SmallImage"]];
        }

        [_tblList performSelectorOnMainThread:@selector(reloadData) withObject:nil waitUntilDone:NO];


}

- (void) beginBackgroundFetchTask
{
   self.backgroundFetchTask = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:^{
        [self endBackgroundFetchTask];
    }];
}

- (void) endBackgroundFetchTask
{
    [[UIApplication sharedApplication] endBackgroundTask: self.backgroundFetchTask];
    self.backgroundFetchTask = UIBackgroundTaskInvalid;
    NSLog(@"ended BackgroundFetchTask");
}

-(void)LoadMoreData
{
   //Fetches new 5 data from server...
}

Solution

  • I think the problem maybe at [self downloadImage_3:indexPath]; you call dispatch_async when you at Button1, and the dispatch_async block have not invoked, then you click on Button2, then I guess msg_array is cleared and filled with new 5 object, after that, dispatch_async block is invoked, the path.row for block is 28, whereas, the msg_array has new array content with 5 new objects, then crash.

    You should cancel dispatch_async before click on other button, which is impossible for dispatch_async , so you can have a judgement in block:

    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    
        [self beginBackgroundFetchTask];
    
        [self downloadImage_3:indexPath buttonIndex:index];
    
        [self endBackgroundFetchTask];
    });
    
    -(void)downloadImage_3:(NSIndexPath *)path buttonIndex:(int)buttonIndex{
         if(self.currentSelectIndex != buttonIndex) return; //skip reloadData if not same index
    
    
            UIImage *img = [[UIImage alloc] initWithData:[NSData dataWithContentsOfURL:[NSURL URLWithString:ImagePath]]];
    
            if (img) {
                [dicImages_msg setObject:img forKey:[[msg_array objectAtIndex:path.row] valueForKey:@"Merchant_SmallImage"]];
            }
    
            [_tblList performSelectorOnMainThread:@selector(reloadData) withObject:nil waitUntilDone:NO];
    
    }