I have been searching for this problem on the SOF for several days and I still have not found the solution (say the same problem) yet.
I'm making and app that downloads 5 images simultaneously in an URL list (each image is on a different server).
I have an ImageDownloader
class subclasses NSOperation
and implements the NSURLConnectionDataDelegate
.
So that I can add an instance of ImageDownloader
to an operationQueue
in the ViewController
and it will run in a separate thread under the operationQueue
. The line that add the downloader to the operationQueue
is here:
downloader = [[ImageDownloader alloc] init];
[downloader downloadImageWithURL:[controller.URList objectForKey:[NSString stringWithFormat:@"%d",downloadIndex]] queue:queue andTag:downloadIndex + 100]; //my custom initialize
downloader.delegate = self;
[queue addOperation:downloader]; //use the addOperation method
Everything works fine in iOS6 but messed up in iOS5 (5.0 on my test device and 5.1 on my SDK), it just doesn't receive any response nor data by performing the methods didReceiveResponse
and didReceiveData
at all (these 2 methods are not jumped in).
After the timeout was exceeded, the runloop jumps into didFailWithError
method and the program stalls.
As I understand, this means the runloop still runs right?
I tried to print out the error
and all I got is: The request timed out
.
When I reduce the number of downloading instances to 2 then it runs, but not with >=3 downloading instances.
One more information is that my network connection does limit the number of connection. But it work fine in iOS6, why it just doesn't work on iOS5?
I can still load the web in the simulator while the app is downloading.
So what kind of problem is this and how can I get over this problem?
Thanks in advance.
*Update:* as there are many classes and the problem's not been clearly detected yet, I will share here the whole project. You can download it directly from here:
DownloadingImage
As I just found out, if you're using credentials there is a chance that the server will reject them randomly every once in a while. So if you have a check to make sure previousFailureCount == 0 then you will most likely have a bug.
I've just figured out where my problem is, but not really understand why.
In my ImageDownloader
class, I set up a runloop with done
and currentRunLoop
variables.
In the main method, I have a while loop for forcing the currentRunLoop
run.
As I remove those "runLoop" stuffs, the app runs smoothly on both iOS6 and iOS5.
So change the entire ImageDownloader.m
with these lines then it works (I commented out some useless (say harmful) lines):
//
// ImageLoader.m
// DownloadImagesTableView
//
// Created by Viet Ta Quoc on 6/25/13.
// Copyright (c) 2013 Viet Ta Quoc. All rights reserved.
//
#import "ImageDownloader.h"
@implementation ImageDownloader
@synthesize downloadData,delegate,queue,done,customTag;
NSRunLoop *currentRunLoop;
-(void)downloadImageWithURL:(NSString *)imageUrl queue:(NSOperationQueue*)opQueue andTag:(int)tag{
self.customTag= tag;
self.queue = opQueue;
// self.done = NO;
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:imageUrl] cachePolicy:NSURLRequestReloadIgnoringCacheData timeoutInterval:30];
NSURLConnection *connection = [[NSURLConnection alloc] initWithRequest:request delegate:self startImmediately:NO];
[connection start];
// currentRunLoop = [NSRunLoop currentRunLoop];
NSLog(@"Start downloading image %d...",customTag);
}
-(void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response{
NSLog(@"Received response...");
downloadData=[[NSMutableData alloc] initWithLength:0];
expectedDataLength=[response expectedContentLength];
NSLog(@"Image %d size: %lld kb",customTag,[response expectedContentLength]/1024);
}
-(void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data{
float receivedLenght = [data length];
receivedDataLength=(receivedDataLength+receivedLenght);
float progress=(float)receivedDataLength/(float)expectedDataLength;
[delegate updateProgess:progress andIndex:[NSIndexPath indexPathForRow:customTag-100 inSection:0]];
[self.downloadData appendData:data];
// NSLog(@"Percentage of data received of tag %d: %f %%",self.customTag,progress*100);
}
-(void)connectionDidFinishLoading:(NSURLConnection *)connection{
[delegate finishedDownloadingImage:downloadData andTag:customTag];
// done = YES;
}
-(void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error{
UIAlertView *alert=[[UIAlertView alloc]initWithTitle:@"Warning" message:@"Network Connection Failed?" delegate:self cancelButtonTitle:@"Cancel" otherButtonTitles:nil, nil];
// NSLog(@"%@",[error debugDescription]);
NSLog(@"Connection failed! Error - %@ %@",[error localizedDescription],[[error userInfo] objectForKey:NSURLErrorFailingURLStringErrorKey]);
[alert show];
}
-(void)connection:(NSURLConnection *)connection didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge{
NSLog(@"Got here *(*&(**&(*&(*&(*&(*&(*&(*&(*&(*&(*&(*&(*&(*&(*&(*&(*&(*&");
}
-(void)main{
// do{
//// NSLog(@"Running....1");
// [currentRunLoop runUntilDate:[NSDate distantFuture]];
// // [currentRunLoop run];
// } while (!done);
// [currentRunLoop runUntilDate:[NSDate dateWithTimeIntervalSinceNow:0.01]];
}
@end
Thank you guys for your supports.
==================================================================================
P/s: for anyone who interested in this problem, I update here my entire solution: DownloadImage_Final