SDWebImage 4 - Simple analysis of internal calls within the framework
The following comments on source code are from
Wen Ding Ding Notes.
UML Diagram and Call Sequence Diagram
Second, we started by setting up a network picture for UIImageView.
First, let's look at the way we call it ourselves.
/*Memory + disk cache*/
-(void)download{
NSString *path = @"http://www.qqpk.cn/Article/UploadFiles/201112/20111208140318990.jpg";
[self.imageView sd_setImageWithURL:[NSURL URLWithString:path] placeholderImage:[UIImage imageNamed:@"image1"] options:0 progress:^(NSInteger receivedSize, NSInteger expectedSize) {
} completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, NSURL *imageURL) {
}];
}
3. sd_setImageWithURL
Now let's go to sd_setImageWithURL in the method we used above.
In fact, this method mainly does the following things
Set up placeholder pictures
-
If the url is not empty, create an operation for SDWeb Image Operation and add it to the operation cache for image loading
The SDWebImageManager.sharedManager downloadImageWithURL is called when the operation is created, and the retrieve is completed.
Judge the call to completedBlock based on whether there are pictures or the status we set up earlier
If the url is empty, call back to the completed Block block passed in and set the image to nil
//The Core Method of Downloading Pictures
/*
* url Picture binary data
* placeholder UIImageView Occupancy Picture of ____________
* options Picture Download Options (Policy)
* progressBlock Progress callback
* completedBlock Finish callback
*/
- (void)sd_setImageWithURL:(NSURL *)url placeholderImage:(UIImage *)placeholder options:(SDWebImageOptions)options progress:(SDWebImageDownloaderProgressBlock)progressBlock completed:(SDWebImageCompletionBlock)completedBlock {
// Cancel the current image download
[self sd_cancelCurrentImageLoad];
// Using runtime retain url
objc_setAssociatedObject(self, &imageURLKey, url, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
//Judge that if the incoming download strategy is not to delay the display of placeholder images, then set the placeholder images in the main thread
if (!(options & SDWebImageDelayPlaceholder)) {
dispatch_main_async_safe(^{
// Set up a placeholder image
self.image = placeholder;
});
}
//If the url is not empty
if (url) {
// check if activityView is enabled or not
//Check whether activityView is available
if ([self showActivityIndicatorView]) {
[self addActivityIndicator];
}
__weak __typeof(self)wself = self;
// Instantiating SDWeb Image Operation Operation
id <SDWebImageOperation> operation = [SDWebImageManager.sharedManager downloadImageWithURL:url options:options progress:progressBlock completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, BOOL finished, NSURL *imageURL) {
//Remove UIActivity Indicator View
[wself removeActivityIndicator];
if (!wself) return;
//The operations in the following block are handled in the main thread
dispatch_main_sync_safe(^{
if (!wself) return;
//If the picture download is completed and the incoming download option is to set the picture manually, the completedBlock callback is executed directly and returned.
if (image && (options & SDWebImageAvoidAutoSetImage) && completedBlock)
{
completedBlock(image, error, cacheType, url);
return;
}
else if (image) { //Otherwise, if the picture exists, set the picture to the UI Image View and refresh the redrawn view
wself.image = image;
[wself setNeedsLayout];
} else {
//If no image is obtained
//If the incoming download option is to delay the display of the placeholder image, set the placeholder image to the UI Image View and refresh the redrawn view
if ((options & SDWebImageDelayPlaceholder)) {
wself.image = placeholder;
[wself setNeedsLayout];
}
}
if (completedBlock && finished) {
completedBlock(image, error, cacheType, url);
}
});
}];
[self sd_setImageLoadOperation:operation forKey:@"UIImageViewImageLoad"];
} else {
//If the url is empty, the following operations are handled in the mainline
dispatch_main_async_safe(^{
//Remove UIActivity Indicator View
[self removeActivityIndicator];
//Processing error messages and performing end-of-task callbacks to pass error messages as parameters
NSError *error = [NSError errorWithDomain:SDWebImageErrorDomain code:-1 userInfo:@{NSLocalizedDescriptionKey : @"Trying to load a nil url"}];
if (completedBlock) {
completedBlock(nil, error, SDImageCacheTypeNone, url);
}
});
}
}
4. Download ImageWithURL
Judge the url (legality, blacklist)
If the URL is incorrect or the download strategy chosen is not "download failed attempt to re-download" and the URL exists in the blacklist, then return directly, call back the task to complete the block, and pass the error message.
Add the current download task to the Current Executing Task Array
-
Find out whether the image cache corresponding to URLKEY exists, and then transfer the image (existence | non-existence) and the image cache method to block after the search.
After the cache situation is searched, the subsequent processing is carried out in the block block.a. Operation cancels, removes the operation added in our three steps from the "Current Executing Task Array" and does not perform the follow-up operation of the block.
b. If the image is not cached, we call the downloadImageWithURL method of SDWebImageDownloader to create the downloadImageWithURL method to download. This callback block handles the caching of the image, callbacks the block, and removes the operations added in our three steps from the "Currently Executing Task Array".
c. If the cached image exists, call back the block, removing the operation added in our three steps from the Current Executing Task Array
d. Pictures are not cached and proxy downloads are not allowed. Call back completedBlock in the main thread to remove the operations added in our three steps from the Current Executing Task Array.
Returns the operation created.
- (id <SDWebImageOperation>)downloadImageWithURL:(NSURL *)url
options:(SDWebImageOptions)options
progress:(SDWebImageDownloaderProgressBlock)progressBlock
completed:(SDWebImageCompletionWithFinishedBlock)completedBlock {
// Invoking this method without a completedBlock is pointless
//Without completedblock, it makes no sense to call this method
NSAssert(completedBlock != nil, @"If you mean to prefetch the image, use -[SDWebImagePrefetcher prefetchURLs] instead");
// Very common mistake is to send the URL using NSString object instead of NSURL. For some strange reason, XCode won't
// throw any warning for this type mismatch. Here we failsafe this error by allowing URLs to be passed as NSString.
//Check that the user's incoming URL is correct, and if the URL is of NSString type, try to convert it
if ([url isKindOfClass:NSString.class]) {
url = [NSURL URLWithString:(NSString *)url];
}
// Prevents app crashing on argument type error like sending NSNull instead of NSURL
//Prevent application crashes due to parameter type errors, and determine whether the URL is NSURL type, or if not, set it directly to nil?
if (![url isKindOfClass:NSURL.class]) {
url = nil;
}
//Initialize an SDWeb Image CombinedOperationBlock block
__block SDWebImageCombinedOperation *operation = [SDWebImageCombinedOperation new];
__weak SDWebImageCombinedOperation *weakOperation = operation;
BOOL isFailedUrl = NO; //The initialization setting for this URL is correct
//Add a mutex to retrieve whether the URL of the requested image is in the collection that failed to download (URL blacklist)
@synchronized (self.failedURLs) {
isFailedUrl = [self.failedURLs containsObject:url];
}
//If the URL is incorrect or the download strategy chosen is not "download failed attempt to re-download" and the URL exists in the blacklist, then return directly, call back the task to complete the block, and pass the error message.
if (url.absoluteString.length == 0 || (!(options & SDWebImageRetryFailed) && isFailedUrl)) {
//This macro ensures that the completedBlock callback is executed in the main thread
dispatch_main_sync_safe(^{
NSError *error = [NSError errorWithDomain:NSURLErrorDomain code:NSURLErrorFileDoesNotExist userInfo:nil];
completedBlock(nil, error, SDImageCacheTypeNone, YES, url);
});
return operation;
}
//Add the current download task to the Current Executing Task Array by adding a mutex lock
@synchronized (self.runningOperations) {
[self.runningOperations addObject:operation];
}
//Get the cache KEY corresponding to the URL
NSString *key = [self cacheKeyForURL:url];
//This method finds whether the image cache corresponding to URLKEY exists or not. After searching, it passes the image (existence | non-existence) and the image cache method in block mode.
//After the cache situation has been searched, follow-up processing is performed in the block block (if the picture does not have a cache, download | if the cache exists | if the user sets the download cache strategy, how to refresh the cache, etc.)
operation.cacheOperation = [self.imageCache queryDiskCacheForKey:key done:^(UIImage *image, SDImageCacheType cacheType) {
//First, determine whether the download operation has been cancelled, and if cancelled, remove the current operation from the runningOperations array and return it directly.
if (operation.isCancelled) {
@synchronized (self.runningOperations) {
[self.runningOperations removeObject:operation];
}
return;
}
//(Image does not exist | | Download strategy is refresh cache) and (shouldDownload Image ForURL cannot respond | | The image has cache)
if ((!image || options & SDWebImageRefreshCached) && (![self.delegate respondsToSelector:@selector(imageManager:shouldDownloadImageForURL:)] || [self.delegate imageManager:self shouldDownloadImageForURL:url])) {
//From here on, downloader Options (download strategy) has been handled.
if (image && options & SDWebImageRefreshCached) { //If the image exists, but the download strategy is to refresh the cache, notify the cache image and try to re-download it.
dispatch_main_sync_safe(^{
// If image was found in the cache but SDWebImageRefreshCached is provided, notify about the cached image
// AND try to re-download it in order to let a chance to NSURLCache to refresh it from server.
completedBlock(image, nil, cacheType, YES, url);
});
}
// download if no image or requested to refresh anyway, and download allowed by delegate
SDWebImageDownloaderOptions downloaderOptions = 0;
//If the download strategy is SDWeb Image LowPriority, downloader Options = itself
if (options & SDWebImageLowPriority) downloaderOptions |= SDWebImageDownloaderLowPriority;
if (options & SDWebImageProgressiveDownload) downloaderOptions |= SDWebImageDownloaderProgressiveDownload;
if (options & SDWebImageRefreshCached) downloaderOptions |= SDWebImageDownloaderUseNSURLCache;
if (options & SDWebImageContinueInBackground) downloaderOptions |= SDWebImageDownloaderContinueInBackground;
if (options & SDWebImageHandleCookies) downloaderOptions |= SDWebImageDownloaderHandleCookies;
if (options & SDWebImageAllowInvalidSSLCertificates) downloaderOptions |= SDWebImageDownloaderAllowInvalidSSLCertificates;
if (options & SDWebImageHighPriority) downloaderOptions |= SDWebImageDownloaderHighPriority;
if (image && options & SDWebImageRefreshCached) { //If the image exists and the download strategy is refresh cache
// force progressive off if image already cached but forced refreshing
//If the image is cached, but the cache needs to be refreshed, the refresh is mandatory
downloaderOptions &= ~SDWebImageDownloaderProgressiveDownload;
// ignore image read from NSURLCache if image if cached but force refreshing
//Ignore reading pictures from NSURLCache
downloaderOptions |= SDWebImageDownloaderIgnoreCachedResponse;
}
//At this location, the downloader Options (i.e., download policy) processing operation ends
//Core Method: Use Downloader to Download Pictures
id <SDWebImageOperation> subOperation = [self.imageDownloader downloadImageWithURL:url options:downloaderOptions progress:progressBlock completed:^(UIImage *downloadedImage, NSData *data, NSError *error, BOOL finished) {
if (weakOperation.isCancelled) {
//If the operation is cancelled at this time, nothing is done.
// Do nothing if the operation was cancelled
// See #699 for more details
// if we would call the completedBlock, there could be a race condition between this block and another completedBlock for the same object, so if this one is called second, we will overwrite the new data
}
else if (error) { //If the download fails, the finished callback is processed, and the corresponding image URL is added to the blacklist where appropriate.
dispatch_main_sync_safe(^{
if (!weakOperation.isCancelled) {
completedBlock(nil, error, SDImageCacheTypeNone, finished, url);
}
});
if ( error.code != NSURLErrorNotConnectedToInternet
&& error.code != NSURLErrorCancelled
&& error.code != NSURLErrorTimedOut
&& error.code != NSURLErrorInternationalRoamingOff
&& error.code != NSURLErrorDataNotAllowed
&& error.code != NSURLErrorCannotFindHost
&& error.code != NSURLErrorCannotConnectToHost) {
@synchronized (self.failedURLs) {
[self.failedURLs addObject:url];
}
}
}
else {//Download successful
//First, determine whether the current download strategy is SDWebImage RetryFailed, and if so, delete the URL from the blacklist.
if ((options & SDWebImageRetryFailed)) {
@synchronized (self.failedURLs) {
[self.failedURLs removeObject:url];
}
}
//Do you want disk caching?
BOOL cacheOnDisk = !(options & SDWebImageCacheMemoryOnly);
//If the download strategy is SDWeb Image RefreshCached and the image cache exists and is not downloaded, then nothing is done.
if (options & SDWebImageRefreshCached && image && !downloadedImage) {
// Image refresh hit the NSURLCache cache, do not call the completion block
}
else if (downloadedImage && (!downloadedImage.images || (options & SDWebImageTransformAnimatedImage)) && [self.delegate respondsToSelector:@selector(imageManager:transformDownloadedImage:withURL:)]) {
//Otherwise, if the downloaded image exists and (not an animated image array | | the download strategy is SDWebImage Transform Animated Image & & transformDownloaded Image method is available)
//Open Subthread Processing
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
//Immediately after download, convert the image and cache the disk and memory
UIImage *transformedImage = [self.delegate imageManager:self transformDownloadedImage:downloadedImage withURL:url];
#warning 2
if (transformedImage && finished) {
BOOL imageWasTransformed = ![transformedImage isEqual:downloadedImage];
[self.imageCache storeImage:transformedImage recalculateFromImage:imageWasTransformed imageData:(imageWasTransformed ? nil : data) forKey:key toDisk:cacheOnDisk];
}
//Callback completedBlock in the main thread
dispatch_main_sync_safe(^{
if (!weakOperation.isCancelled) {
completedBlock(transformedImage, nil, SDImageCacheTypeNone, finished, url);
}
});
});
}
else {
//Cache the downloaded image when it is complete
if (downloadedImage && finished) {
[self.imageCache storeImage:downloadedImage recalculateFromImage:NO imageData:data forKey:key toDisk:cacheOnDisk];
}
dispatch_main_sync_safe(^{
if (!weakOperation.isCancelled) {
completedBlock(downloadedImage, nil, SDImageCacheTypeNone, finished, url);
}
});
}
}
if (finished) {
@synchronized (self.runningOperations) {
[self.runningOperations removeObject:operation];
}
}
}];
//Handling cancelBlock
operation.cancelBlock = ^{
[subOperation cancel];
@synchronized (self.runningOperations) {
[self.runningOperations removeObject:weakOperation];
}
};
}
else if (image) { //If the picture exists and the operation is not cancelled, call back completedBlock in the main thread and remove the current operation.
dispatch_main_sync_safe(^{
if (!weakOperation.isCancelled) {
completedBlock(image, nil, cacheType, YES, url);
}
});
@synchronized (self.runningOperations) {
[self.runningOperations removeObject:operation];
}
}
else {
// Image not in cache and download disallowed by delegate
//If the image does not have a cache and the proxy is not allowed to download, call back the completedBlock in the main thread and remove the current operation.
dispatch_main_sync_safe(^{
if (!weakOperation.isCancelled) {
completedBlock(nil, nil, SDImageCacheTypeNone, YES, url);
}
});
@synchronized (self.runningOperations) {
[self.runningOperations removeObject:operation];
}
}
}];
return operation;
}
5. Download ImageWithURL
Processing download timeout, if not set, initialized to 15 seconds
Create a request for NSMutableURLRequest, but not yet
Call initWithRequest of SDWeb Image Downloader Operation to create an operation to download images. Download part of the method to call back our progress. After the download is completed, a series of judgments are made and callbacks are invoked.
Return operation
//Core Method: Downloading Pictures
- (id <SDWebImageOperation>)downloadImageWithURL:(NSURL *)url options:(SDWebImageDownloaderOptions)options progress:(SDWebImageDownloaderProgressBlock)progressBlock completed:(SDWebImageDownloaderCompletedBlock)completedBlock {
__block SDWebImageDownloaderOperation *operation;
__weak __typeof(self)wself = self; //To avoid circular references to block s
//Processing progress callback | Completion callback, etc., call the createCallback block if the url does not exist in self.URLCallbacks
[self addProgressCallback:progressBlock completedBlock:completedBlock forURL:url createCallback:^{
//Processing download timeout, if not set, initialized to 15 seconds
NSTimeInterval timeoutInterval = wself.downloadTimeout;
if (timeoutInterval == 0.0) {
timeoutInterval = 15.0;
}
// In order to prevent from potential duplicate caching (NSURLCache + SDImageCache) we disable the cache for image requests if told otherwise
//Create variable request objects and set request timeouts based on a given URL and caching policy
//Request Policy: If SDWeb Image Downloader UseNSURLCache, use NSURLRequest UseProtocolCache Policy, otherwise use NSURLRequest Reload Ignoring Local Cache Data
/*
NSURLRequestUseProtocolCachePolicy:Default Caching Policy
1)If the cache does not exist, it is retrieved directly from the server.
2)If the cache exists, the next operation will be judged according to the Cache-Control field in response. For example, if the Cache-Control field is must-revalidata, the server will be asked if the data is updated, and if it is not updated, the data will be returned directly to the user's cache. If it has been updated, the server will be requested.
NSURLRequestReloadIgnoringLocalCacheData:Ignore the local cache data and request the server directly.
*/
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:url cachePolicy:(options & SDWebImageDownloaderUseNSURLCache ? NSURLRequestUseProtocolCachePolicy : NSURLRequestReloadIgnoringLocalCacheData) timeoutInterval:timeoutInterval];
//Set whether to use Cookies (using bitwise and)
/*
Reference on cookies: http://blog.csdn.net/chun799/article/details/17206907
*/
request.HTTPShouldHandleCookies = (options & SDWebImageDownloaderHandleCookies);
//Open the HTTP pipeline, which can significantly reduce the load time of requests, but is disabled by default because it is not widely supported by the server
request.HTTPShouldUsePipelining = YES;
//Setting request header information (filtering, etc.)
if (wself.headersFilter) {
request.allHTTPHeaderFields = wself.headersFilter(url, [wself.HTTPHeaders copy]);
}
else {
request.allHTTPHeaderFields = wself.HTTPHeaders;
}
//Core Method: Create the Operation of Downloading Pictures
operation = [[wself.operationClass alloc] initWithRequest:request
options:options
progress:^(NSInteger receivedSize, NSInteger expectedSize) {
SDWebImageDownloader *sself = wself;
if (!sself) return;
__block NSArray *callbacksForURL;
dispatch_sync(sself.barrierQueue, ^{
callbacksForURL = [sself.URLCallbacks[url] copy];
});
//Traverse through all dictionaries in the callbacks ForURL array and execute the SDWeb Image Downloader ProgressBlock callback
for (NSDictionary *callbacks in callbacksForURL) {
//Note: The author of SDWeb Image Downloader ProgressBlock may have handled callbacks in the main thread, considering that users will refresh when they get progress data.
dispatch_async(dispatch_get_main_queue(), ^{
SDWebImageDownloaderProgressBlock callback = callbacks[kProgressCallbackKey];
if (callback) callback(receivedSize, expectedSize);
});
}
}
completed:^(UIImage *image, NSData *data, NSError *error, BOOL finished) {
SDWebImageDownloader *sself = wself;
if (!sself) return;
__block NSArray *callbacksForURL;
dispatch_barrier_sync(sself.barrierQueue, ^{
callbacksForURL = [sself.URLCallbacks[url] copy];
//If done, remove the URL from the URLCallbacks dictionary
if (finished) {
[sself.URLCallbacks removeObjectForKey:url];
}
});
//Traverse through all dictionaries in the callbacks ForURL array and execute the SDWeb Image Downloader Completed Block callback
for (NSDictionary *callbacks in callbacksForURL) {
SDWebImageDownloaderCompletedBlock callback = callbacks[kCompletedCallbackKey];
if (callback) callback(image, data, error, finished);
}
}
cancelled:^{
SDWebImageDownloader *sself = wself;
if (!sself) return;
//Remove the current url from the URLCallbacks dictionary
dispatch_barrier_async(sself.barrierQueue, ^{
[sself.URLCallbacks removeObjectForKey:url];
});
}];
//Set whether decoding is required
operation.shouldDecompressImages = wself.shouldDecompressImages;
//identity authentication
if (wself.urlCredential) {
operation.credential = wself.urlCredential;
} else if (wself.username && wself.password) {
//Setting credentials for authentication when https is accessed
operation.credential = [NSURLCredential credentialWithUser:wself.username password:wself.password persistence:NSURLCredentialPersistenceForSession];
}
//Determine whether the download strategy is of high or low priority to set the queue priority of the operation?
if (options & SDWebImageDownloaderHighPriority) {
operation.queuePriority = NSOperationQueuePriorityHigh;
} else if (options & SDWebImageDownloaderLowPriority) {
operation.queuePriority = NSOperationQueuePriorityLow;
}
//Add the download operation to the download queue
//This method calls the start method inside the operation to open the download task of the picture.
[wself.downloadQueue addOperation:operation];
//Judge the priority of task execution. If it is LIFO, adjust the dependency of task and give priority to current (last added) task.
if (wself.executionOrder == SDWebImageDownloaderLIFOExecutionOrder) {
// Emulate LIFO execution order by systematically adding new operations as last operation's dependency
[wself.lastAddedOperation addDependency:operation];
wself.lastAddedOperation = operation;//Set the current download operation to the last one
}
}];
return operation;
}
VI. initWithRequest
Some operations are initialized in initWithRequest
We know that the start method is called internally in NSOperation. The download operation is put in the start.
//Method of Initializing operation
- (id)initWithRequest:(NSURLRequest *)request
options:(SDWebImageDownloaderOptions)options
progress:(SDWebImageDownloaderProgressBlock)progressBlock
completed:(SDWebImageDownloaderCompletedBlock)completedBlock
cancelled:(SDWebImageNoParamsBlock)cancelBlock {
if ((self = [super init])) {
_request = request;
_shouldDecompressImages = YES;
_shouldUseCredentialStorage = YES;
_options = options;
_progressBlock = [progressBlock copy];
_completedBlock = [completedBlock copy];
_cancelBlock = [cancelBlock copy];
_executing = NO;
_finished = NO;
_expectedSize = 0;
responseFromCached = YES; // Initially wrong until `connection:willCacheResponse:` is called or not called
}
return self;
}
//Core Method: Processing Picture Download Operation in this Method
- (void)start {
@synchronized (self) {
//Determine whether the current operation has been cancelled, if cancelled, mark the end of the task, and process subsequent block s and cleanup operations
if (self.isCancelled) {
self.finished = YES;
[self reset];
return;
}
//Conditional compilation, if it is an iphone device and larger than 4.0
#if TARGET_OS_IPHONE && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_4_0
Class UIApplicationClass = NSClassFromString(@"UIApplication");
BOOL hasApplication = UIApplicationClass && [UIApplicationClass respondsToSelector:@selector(sharedApplication)];
//The program is about to enter the background.
if (hasApplication && [self shouldContinueWhenAppEntersBackground]) {
__weak __typeof__ (self) wself = self;
//Get the UI Application singleton object
UIApplication * app = [UIApplicationClass performSelector:@selector(sharedApplication)];
//UIBackgroundTaskIdentifier: Running programs in the background in limited time can be achieved through UIBackgroundTaskIdentifier
//Get a certain amount of time in the background to refer to our code
self.backgroundTaskId = [app beginBackgroundTaskWithExpirationHandler:^{
__strong __typeof (wself) sself = wself;
#warning 3
if (sself) {
[sself cancel]; //Cancel the current download operation
[app endBackgroundTask:sself.backgroundTaskId]; //End-of-stage tasks
sself.backgroundTaskId = UIBackgroundTaskInvalid;
}
}];
}
#endif
self.executing = YES; //The current task is in progress
//Create the NSURLConnection object and set up the proxy (no request is sent immediately)
self.connection = [[NSURLConnection alloc] initWithRequest:self.request delegate:self startImmediately:NO];
//Get the current thread
self.thread = [NSThread currentThread];
}
[self.connection start]; //Sending network requests
if (self.connection) {
if (self.progressBlock) {
//Callback to progress block
self.progressBlock(0, NSURLResponseUnknownLength);
}
//Register the notification center to send notifications to SDWebImage Download Start Notification in the main thread
dispatch_async(dispatch_get_main_queue(), ^{
[[NSNotificationCenter defaultCenter] postNotificationName:SDWebImageDownloadStartNotification object:self];
});
//Open the Runloop corresponding to the thread
if (floor(NSFoundationVersionNumber) <= NSFoundationVersionNumber_iOS_5_1) {
// Make sure to run the runloop in our background thread so it can process downloaded data
//Ensure that the runloop of the background thread runs
// Note: we use a timeout to work around an issue with NSURLConnection cancel under iOS 5
// not waking up the runloop, leading to dead threads (see https://github.com/rs/SDWebImage/issues/466)
CFRunLoopRunInMode(kCFRunLoopDefaultMode, 10, false);
}
else {
//Open Runloop
CFRunLoopRun();
}
if (!self.isFinished) {
[self.connection cancel]; //Cancel Network Connection
//Handling error messages
[self connection:self.connection didFailWithError:[NSError errorWithDomain:NSURLErrorDomain code:NSURLErrorTimedOut userInfo:@{NSURLErrorFailingURLErrorKey : self.request.URL}]];
}
}
else {
//Print Connection Initialization Failed by Performing completedBlock Callback
if (self.completedBlock) {
self.completedBlock(nil, nil, [NSError errorWithDomain:NSURLErrorDomain code:0 userInfo:@{NSLocalizedDescriptionKey : @"Connection can't be initialized"}], YES);
}
}
#if TARGET_OS_IPHONE && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_4_0
Class UIApplicationClass = NSClassFromString(@"UIApplication");
if(!UIApplicationClass || ![UIApplicationClass respondsToSelector:@selector(sharedApplication)]) {
return;
}
if (self.backgroundTaskId != UIBackgroundTaskInvalid) {
UIApplication * app = [UIApplication performSelector:@selector(sharedApplication)];
[app endBackgroundTask:self.backgroundTaskId];
self.backgroundTaskId = UIBackgroundTaskInvalid;
}
#endif
}