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...
}
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];
}