Browse Source

Fixed race condition with image downloader when starting/cancelling/starting the same request

Alternate Solution

See https://github.com/Alamofire/AlamofireImage/issues/55 for details
Kevin Harwood 9 years ago
parent
commit
43d4847e39

+ 4 - 0
AFNetworking.xcodeproj/project.pbxproj

@@ -7,6 +7,7 @@
 	objects = {
 	objects = {
 
 
 /* Begin PBXBuildFile section */
 /* Begin PBXBuildFile section */
+		2960BAC31C1B2F1A00BA02F0 /* AFUIButtonTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 2960BAC21C1B2F1A00BA02F0 /* AFUIButtonTests.m */; };
 		297824A31BC2D69A0041C395 /* adn_0.cer in Resources */ = {isa = PBXBuildFile; fileRef = 297824A01BC2D69A0041C395 /* adn_0.cer */; };
 		297824A31BC2D69A0041C395 /* adn_0.cer in Resources */ = {isa = PBXBuildFile; fileRef = 297824A01BC2D69A0041C395 /* adn_0.cer */; };
 		297824A41BC2D69A0041C395 /* adn_0.cer in Resources */ = {isa = PBXBuildFile; fileRef = 297824A01BC2D69A0041C395 /* adn_0.cer */; };
 		297824A41BC2D69A0041C395 /* adn_0.cer in Resources */ = {isa = PBXBuildFile; fileRef = 297824A01BC2D69A0041C395 /* adn_0.cer */; };
 		297824A51BC2D69A0041C395 /* adn_1.cer in Resources */ = {isa = PBXBuildFile; fileRef = 297824A11BC2D69A0041C395 /* adn_1.cer */; };
 		297824A51BC2D69A0041C395 /* adn_1.cer in Resources */ = {isa = PBXBuildFile; fileRef = 297824A11BC2D69A0041C395 /* adn_1.cer */; };
@@ -216,6 +217,7 @@
 /* End PBXContainerItemProxy section */
 /* End PBXContainerItemProxy section */
 
 
 /* Begin PBXFileReference section */
 /* Begin PBXFileReference section */
+		2960BAC21C1B2F1A00BA02F0 /* AFUIButtonTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AFUIButtonTests.m; sourceTree = "<group>"; };
 		297824A01BC2D69A0041C395 /* adn_0.cer */ = {isa = PBXFileReference; lastKnownFileType = file; name = adn_0.cer; path = ADNNetServerTrustChain/adn_0.cer; sourceTree = "<group>"; };
 		297824A01BC2D69A0041C395 /* adn_0.cer */ = {isa = PBXFileReference; lastKnownFileType = file; name = adn_0.cer; path = ADNNetServerTrustChain/adn_0.cer; sourceTree = "<group>"; };
 		297824A11BC2D69A0041C395 /* adn_1.cer */ = {isa = PBXFileReference; lastKnownFileType = file; name = adn_1.cer; path = ADNNetServerTrustChain/adn_1.cer; sourceTree = "<group>"; };
 		297824A11BC2D69A0041C395 /* adn_1.cer */ = {isa = PBXFileReference; lastKnownFileType = file; name = adn_1.cer; path = ADNNetServerTrustChain/adn_1.cer; sourceTree = "<group>"; };
 		297824A21BC2D69A0041C395 /* adn_2.cer */ = {isa = PBXFileReference; lastKnownFileType = file; name = adn_2.cer; path = ADNNetServerTrustChain/adn_2.cer; sourceTree = "<group>"; };
 		297824A21BC2D69A0041C395 /* adn_2.cer */ = {isa = PBXFileReference; lastKnownFileType = file; name = adn_2.cer; path = ADNNetServerTrustChain/adn_2.cer; sourceTree = "<group>"; };
@@ -440,6 +442,7 @@
 				298D7C8C1BC2C88F00FD3B3E /* AFUIActivityIndicatorViewTests.m */,
 				298D7C8C1BC2C88F00FD3B3E /* AFUIActivityIndicatorViewTests.m */,
 				298D7C8D1BC2C88F00FD3B3E /* AFUIImageViewTests.m */,
 				298D7C8D1BC2C88F00FD3B3E /* AFUIImageViewTests.m */,
 				298D7C8E1BC2C88F00FD3B3E /* AFUIRefreshControlTests.m */,
 				298D7C8E1BC2C88F00FD3B3E /* AFUIRefreshControlTests.m */,
+				2960BAC21C1B2F1A00BA02F0 /* AFUIButtonTests.m */,
 			);
 			);
 			name = "AFNetworking UIKit Tests";
 			name = "AFNetworking UIKit Tests";
 			sourceTree = "<group>";
 			sourceTree = "<group>";
@@ -957,6 +960,7 @@
 			isa = PBXSourcesBuildPhase;
 			isa = PBXSourcesBuildPhase;
 			buildActionMask = 2147483647;
 			buildActionMask = 2147483647;
 			files = (
 			files = (
+				2960BAC31C1B2F1A00BA02F0 /* AFUIButtonTests.m in Sources */,
 				298D7C961BC2C94400FD3B3E /* AFTestCase.m in Sources */,
 				298D7C961BC2C94400FD3B3E /* AFTestCase.m in Sources */,
 				298D7CB11BC2CA6E00FD3B3E /* AFHTTPRequestSerializationTests.m in Sources */,
 				298D7CB11BC2CA6E00FD3B3E /* AFHTTPRequestSerializationTests.m in Sources */,
 				297824AE1BC2DBD80041C395 /* AFUIActivityIndicatorViewTests.m in Sources */,
 				297824AE1BC2DBD80041C395 /* AFUIActivityIndicatorViewTests.m in Sources */,

+ 101 - 0
Tests/Tests/AFUIButtonTests.m

@@ -0,0 +1,101 @@
+// AFUIButtonTests.h
+// Copyright (c) 2011–2015 Alamofire Software Foundation (http://alamofire.org/)
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+#import <XCTest/XCTest.h>
+#import "AFTestCase.h"
+#import "UIButton+AFNetworking.h"
+#import "AFImageDownloader.h"
+
+@interface AFUIButtonTests : AFTestCase
+@property (nonatomic, strong) UIImage *cachedImage;
+@property (nonatomic, strong) NSURLRequest *cachedImageRequest;
+@property (nonatomic, strong) UIButton *button;
+
+@property (nonatomic, strong) NSURL *error404URL;
+@property (nonatomic, strong) NSURLRequest *error404URLRequest;
+
+@property (nonatomic, strong) NSURL *jpegURL;
+@property (nonatomic, strong) NSURLRequest *jpegURLRequest;
+@end
+
+@implementation AFUIButtonTests
+
+- (void)setUp {
+    [super setUp];
+    [[UIButton sharedImageDownloader].imageCache removeAllImages];
+    [[[[[[UIButton sharedImageDownloader] sessionManager] session] configuration] URLCache] removeAllCachedResponses];
+    [UIButton setSharedImageDownloader:[[AFImageDownloader alloc] init]];
+
+    self.button = [UIButton new];
+
+    self.jpegURL = [NSURL URLWithString:@"https://httpbin.org/image/jpeg"];
+    self.jpegURLRequest = [NSURLRequest requestWithURL:self.jpegURL];
+
+    self.error404URL = [NSURL URLWithString:@"https://httpbin.org/status/404"];
+    self.error404URLRequest = [NSURLRequest requestWithURL:self.error404URL];
+
+}
+
+- (void)tearDown {
+    self.button = nil;
+    [super tearDown];
+    
+}
+
+- (void)testThatForegroundImageCanBeCancelledAndDownloadedImmediately {
+    //https://github.com/Alamofire/AlamofireImage/issues/55
+    XCTestExpectation *expectation = [self expectationWithDescription:@"Request should succeed"];
+    [self.button setImageForState:UIControlStateNormal withURL:self.jpegURL];
+    [self.button cancelImageDownloadTaskForState:UIControlStateNormal];
+    __block UIImage *responseImage;
+    [self.button
+     setImageForState:UIControlStateNormal
+     withURLRequest:self.jpegURLRequest
+     placeholderImage:nil
+     success:^(NSURLRequest * _Nonnull request, NSHTTPURLResponse * _Nullable response, UIImage * _Nonnull image) {
+         responseImage = image;
+         [expectation fulfill];
+     }
+     failure:nil];
+    [self waitForExpectationsWithCommonTimeoutUsingHandler:nil];
+    XCTAssertNotNil(responseImage);
+}
+
+- (void)testThatBackgroundImageCanBeCancelledAndDownloadedImmediately {
+    //https://github.com/Alamofire/AlamofireImage/issues/55
+    XCTestExpectation *expectation = [self expectationWithDescription:@"Request should succeed"];
+    [self.button setBackgroundImageForState:UIControlStateNormal withURL:self.jpegURL];
+    [self.button cancelBackgroundImageDownloadTaskForState:UIControlStateNormal];
+    __block UIImage *responseImage;
+    [self.button
+     setBackgroundImageForState:UIControlStateNormal
+     withURLRequest:self.jpegURLRequest
+     placeholderImage:nil
+     success:^(NSURLRequest * _Nonnull request, NSHTTPURLResponse * _Nullable response, UIImage * _Nonnull image) {
+         responseImage = image;
+         [expectation fulfill];
+     }
+     failure:nil];
+    [self waitForExpectationsWithCommonTimeoutUsingHandler:nil];
+    XCTAssertNotNil(responseImage);
+}
+
+@end

+ 20 - 0
Tests/Tests/AFUIImageViewTests.m

@@ -124,6 +124,24 @@
     XCTAssertEqual(cachedImage, downloadImage);
     XCTAssertEqual(cachedImage, downloadImage);
 }
 }
 
 
+- (void)testThatImageCanBeCancelledAndDownloadedImmediately {
+    //https://github.com/Alamofire/AlamofireImage/issues/55
+    XCTestExpectation *expectation = [self expectationWithDescription:@"Request should succeed"];
+    [self.imageView setImageWithURL:self.jpegURL];
+    [self.imageView cancelImageDownloadTask];
+    __block UIImage *responseImage;
+    [self.imageView
+     setImageWithURLRequest:self.jpegURLRequest
+     placeholderImage:nil
+     success:^(NSURLRequest * _Nonnull request, NSHTTPURLResponse * _Nullable response, UIImage * _Nonnull image) {
+         responseImage = image;
+         [expectation fulfill];
+     }
+     failure:nil];
+    [self waitForExpectationsWithCommonTimeoutUsingHandler:nil];
+    XCTAssertNotNil(responseImage);
+}
+
 - (void)testThatNilURLDoesntCrash {
 - (void)testThatNilURLDoesntCrash {
 #pragma clang diagnostic push
 #pragma clang diagnostic push
 #pragma clang diagnostic ignored "-Wnonnull"
 #pragma clang diagnostic ignored "-Wnonnull"
@@ -132,4 +150,6 @@
 
 
 }
 }
 
 
+
+
 @end
 @end

+ 22 - 2
UIKit+AFNetworking/AFImageDownloader.h

@@ -91,7 +91,7 @@ typedef NS_ENUM(NSInteger, AFImageDownloadPrioritization) {
 /**
 /**
  Initializes the `AFImageDownloader` instance with the given session manager, download prioritization, maximum active download count and image cache.
  Initializes the `AFImageDownloader` instance with the given session manager, download prioritization, maximum active download count and image cache.
 
 
- @param sessionManager The session manager to use to download images
+ @param sessionManager The session manager to use to download images.
  @param downloadPrioritization The download prioritization of the download queue.
  @param downloadPrioritization The download prioritization of the download queue.
  @param maximumActiveDownloads  The maximum number of active downloads allowed at any given time. Recommend `4`.
  @param maximumActiveDownloads  The maximum number of active downloads allowed at any given time. Recommend `4`.
  @param imageCache The image cache used to store all downloaded images in.
  @param imageCache The image cache used to store all downloaded images in.
@@ -114,13 +114,33 @@ typedef NS_ENUM(NSInteger, AFImageDownloadPrioritization) {
  @param success A block to be executed when the image data task finishes successfully. This block has no return value and takes three arguments: the request sent from the client, the response received from the server, and the image created from the response data of request. If the image was returned from cache, the response parameter will be `nil`.
  @param success A block to be executed when the image data task finishes successfully. This block has no return value and takes three arguments: the request sent from the client, the response received from the server, and the image created from the response data of request. If the image was returned from cache, the response parameter will be `nil`.
  @param failure A block object to be executed when the image data task finishes unsuccessfully, or that finishes successfully. This block has no return value and takes three arguments: the request sent from the client, the response received from the server, and the error object describing the network or parsing error that occurred.
  @param failure A block object to be executed when the image data task finishes unsuccessfully, or that finishes successfully. This block has no return value and takes three arguments: the request sent from the client, the response received from the server, and the error object describing the network or parsing error that occurred.
 
 
- @return The image download receipt for the data task if available. `nil` if the image is stored in the image
+ @return The image download receipt for the data task if available. `nil` if the image is stored in the cache.
  cache and the URL request cache policy allows the cache to be used.
  cache and the URL request cache policy allows the cache to be used.
  */
  */
 - (nullable AFImageDownloadReceipt *)downloadImageForURLRequest:(NSURLRequest *)request
 - (nullable AFImageDownloadReceipt *)downloadImageForURLRequest:(NSURLRequest *)request
                                                         success:(nullable void (^)(NSURLRequest *request, NSHTTPURLResponse  * _Nullable response, UIImage *responseObject))success
                                                         success:(nullable void (^)(NSURLRequest *request, NSHTTPURLResponse  * _Nullable response, UIImage *responseObject))success
                                                         failure:(nullable void (^)(NSURLRequest *request, NSHTTPURLResponse * _Nullable response, NSError *error))failure;
                                                         failure:(nullable void (^)(NSURLRequest *request, NSHTTPURLResponse * _Nullable response, NSError *error))failure;
 
 
+/**
+ Creates a data task using the `sessionManager` instance for the specified URL request.
+
+ If the same data task is already in the queue or currently being downloaded, the success and failure blocks are
+ appended to the already existing task. Once the task completes, all success or failure blocks attached to the
+ task are executed in the order they were added.
+
+ @param request The URL request.
+ @param request The identifier to use for the download receipt that will be created for this request. This must be a unique identifier that does not represent any other request.
+ @param success A block to be executed when the image data task finishes successfully. This block has no return value and takes three arguments: the request sent from the client, the response received from the server, and the image created from the response data of request. If the image was returned from cache, the response parameter will be `nil`.
+ @param failure A block object to be executed when the image data task finishes unsuccessfully, or that finishes successfully. This block has no return value and takes three arguments: the request sent from the client, the response received from the server, and the error object describing the network or parsing error that occurred.
+
+ @return The image download receipt for the data task if available. `nil` if the image is stored in the cache.
+ cache and the URL request cache policy allows the cache to be used.
+ */
+- (nullable AFImageDownloadReceipt *)downloadImageForURLRequest:(NSURLRequest *)request
+                                                 withReceiptID:(NSUUID *)receiptID
+                                                        success:(nullable void (^)(NSURLRequest *request, NSHTTPURLResponse  * _Nullable response, UIImage *responseObject))success
+                                                        failure:(nullable void (^)(NSURLRequest *request, NSHTTPURLResponse * _Nullable response, NSError *error))failure;
+
 /**
 /**
  Cancels the data task in the receipt by removing the corresponding success and failure blocks and cancelling the data task if necessary.
  Cancels the data task in the receipt by removing the corresponding success and failure blocks and cancelling the data task if necessary.
 
 

+ 10 - 4
UIKit+AFNetworking/AFImageDownloader.m

@@ -175,9 +175,15 @@
 }
 }
 
 
 - (nullable AFImageDownloadReceipt *)downloadImageForURLRequest:(NSURLRequest *)request
 - (nullable AFImageDownloadReceipt *)downloadImageForURLRequest:(NSURLRequest *)request
+                                                        success:(void (^)(NSURLRequest * _Nonnull, NSHTTPURLResponse * _Nullable, UIImage * _Nonnull))success
+                                                        failure:(void (^)(NSURLRequest * _Nonnull, NSHTTPURLResponse * _Nullable, NSError * _Nonnull))failure {
+    return [self downloadImageForURLRequest:request withReceiptID:[NSUUID UUID] success:success failure:failure];
+}
+
+- (nullable AFImageDownloadReceipt *)downloadImageForURLRequest:(NSURLRequest *)request
+                                                 withReceiptID:(nonnull NSUUID *)receiptID
                                                         success:(nullable void (^)(NSURLRequest *request, NSHTTPURLResponse  * _Nullable response, UIImage *responseObject))success
                                                         success:(nullable void (^)(NSURLRequest *request, NSHTTPURLResponse  * _Nullable response, UIImage *responseObject))success
                                                         failure:(nullable void (^)(NSURLRequest *request, NSHTTPURLResponse * _Nullable response, NSError *error))failure {
                                                         failure:(nullable void (^)(NSURLRequest *request, NSHTTPURLResponse * _Nullable response, NSError *error))failure {
-    NSUUID *callerUUID = [NSUUID UUID];
     __block NSURLSessionDataTask *task = nil;
     __block NSURLSessionDataTask *task = nil;
     dispatch_sync(self.synchronizationQueue, ^{
     dispatch_sync(self.synchronizationQueue, ^{
         NSString *identifier = request.URL.absoluteString;
         NSString *identifier = request.URL.absoluteString;
@@ -185,7 +191,7 @@
         // 1) Append the success and failure blocks to a pre-existing request if it already exists
         // 1) Append the success and failure blocks to a pre-existing request if it already exists
         AFImageDownloaderMergedTask *existingMergedTask = self.mergedTasks[identifier];
         AFImageDownloaderMergedTask *existingMergedTask = self.mergedTasks[identifier];
         if (existingMergedTask != nil) {
         if (existingMergedTask != nil) {
-            AFImageDownloaderResponseHandler *handler = [[AFImageDownloaderResponseHandler alloc] initWithUUID:callerUUID success:success failure:failure];
+            AFImageDownloaderResponseHandler *handler = [[AFImageDownloaderResponseHandler alloc] initWithUUID:receiptID success:success failure:failure];
             [existingMergedTask addResponseHandler:handler];
             [existingMergedTask addResponseHandler:handler];
             task = existingMergedTask.task;
             task = existingMergedTask.task;
             return;
             return;
@@ -247,7 +253,7 @@
                        }];
                        }];
 
 
         // 4) Store the response handler for use when the request completes
         // 4) Store the response handler for use when the request completes
-        AFImageDownloaderResponseHandler *handler = [[AFImageDownloaderResponseHandler alloc] initWithUUID:callerUUID
+        AFImageDownloaderResponseHandler *handler = [[AFImageDownloaderResponseHandler alloc] initWithUUID:receiptID
                                                                                                    success:success
                                                                                                    success:success
                                                                                                    failure:failure];
                                                                                                    failure:failure];
         AFImageDownloaderMergedTask *mergedTask = [[AFImageDownloaderMergedTask alloc]
         AFImageDownloaderMergedTask *mergedTask = [[AFImageDownloaderMergedTask alloc]
@@ -266,7 +272,7 @@
         task = mergedTask.task;
         task = mergedTask.task;
     });
     });
     if (task) {
     if (task) {
-        return [[AFImageDownloadReceipt alloc] initWithReceiptID:callerUUID task:task];
+        return [[AFImageDownloadReceipt alloc] initWithReceiptID:receiptID task:task];
     } else {
     } else {
         return nil;
         return nil;
     }
     }

+ 8 - 4
UIKit+AFNetworking/UIButton+AFNetworking.m

@@ -161,12 +161,14 @@ static const char * af_backgroundImageDownloadReceiptKeyForState(UIControlState
         }
         }
 
 
         __weak __typeof(self)weakSelf = self;
         __weak __typeof(self)weakSelf = self;
+        NSUUID *downloadID = [NSUUID UUID];
         AFImageDownloadReceipt *receipt;
         AFImageDownloadReceipt *receipt;
         receipt = [downloader
         receipt = [downloader
                    downloadImageForURLRequest:urlRequest
                    downloadImageForURLRequest:urlRequest
+                   withReceiptID:downloadID
                    success:^(NSURLRequest * _Nonnull request, NSHTTPURLResponse * _Nullable response, UIImage * _Nonnull responseObject) {
                    success:^(NSURLRequest * _Nonnull request, NSHTTPURLResponse * _Nullable response, UIImage * _Nonnull responseObject) {
                        __strong __typeof(weakSelf)strongSelf = weakSelf;
                        __strong __typeof(weakSelf)strongSelf = weakSelf;
-                       if ([strongSelf isActiveTaskURLEqualToURLRequest:request forState:state]) {
+                       if ([[strongSelf af_imageDownloadReceiptForState:state].receiptID isEqual:downloadID]) {
                            if (success) {
                            if (success) {
                                success(request, response, responseObject);
                                success(request, response, responseObject);
                            } else if(responseObject) {
                            } else if(responseObject) {
@@ -178,7 +180,7 @@ static const char * af_backgroundImageDownloadReceiptKeyForState(UIControlState
                    }
                    }
                    failure:^(NSURLRequest * _Nonnull request, NSHTTPURLResponse * _Nullable response, NSError * _Nonnull error) {
                    failure:^(NSURLRequest * _Nonnull request, NSHTTPURLResponse * _Nullable response, NSError * _Nonnull error) {
                        __strong __typeof(weakSelf)strongSelf = weakSelf;
                        __strong __typeof(weakSelf)strongSelf = weakSelf;
-                       if ([strongSelf isActiveTaskURLEqualToURLRequest:request forState:state]) {
+                       if ([[strongSelf af_imageDownloadReceiptForState:state].receiptID isEqual:downloadID]) {
                            if (failure) {
                            if (failure) {
                                failure(request, response, error);
                                failure(request, response, error);
                            }
                            }
@@ -238,12 +240,14 @@ static const char * af_backgroundImageDownloadReceiptKeyForState(UIControlState
         }
         }
 
 
         __weak __typeof(self)weakSelf = self;
         __weak __typeof(self)weakSelf = self;
+        NSUUID *downloadID = [NSUUID UUID];
         AFImageDownloadReceipt *receipt;
         AFImageDownloadReceipt *receipt;
         receipt = [downloader
         receipt = [downloader
                    downloadImageForURLRequest:urlRequest
                    downloadImageForURLRequest:urlRequest
+                   withReceiptID:downloadID
                    success:^(NSURLRequest * _Nonnull request, NSHTTPURLResponse * _Nullable response, UIImage * _Nonnull responseObject) {
                    success:^(NSURLRequest * _Nonnull request, NSHTTPURLResponse * _Nullable response, UIImage * _Nonnull responseObject) {
                        __strong __typeof(weakSelf)strongSelf = weakSelf;
                        __strong __typeof(weakSelf)strongSelf = weakSelf;
-                       if ([strongSelf isActiveBackgroundTaskURLEqualToURLRequest:request forState:state]) {
+                       if ([[strongSelf af_backgroundImageDownloadReceiptForState:state].receiptID isEqual:downloadID]) {
                            if (success) {
                            if (success) {
                                success(request, response, responseObject);
                                success(request, response, responseObject);
                            } else if(responseObject) {
                            } else if(responseObject) {
@@ -255,7 +259,7 @@ static const char * af_backgroundImageDownloadReceiptKeyForState(UIControlState
                    }
                    }
                    failure:^(NSURLRequest * _Nonnull request, NSHTTPURLResponse * _Nullable response, NSError * _Nonnull error) {
                    failure:^(NSURLRequest * _Nonnull request, NSHTTPURLResponse * _Nullable response, NSError * _Nonnull error) {
                        __strong __typeof(weakSelf)strongSelf = weakSelf;
                        __strong __typeof(weakSelf)strongSelf = weakSelf;
-                       if ([strongSelf isActiveBackgroundTaskURLEqualToURLRequest:request forState:state]) {
+                       if ([[strongSelf af_backgroundImageDownloadReceiptForState:state].receiptID isEqual:downloadID]) {
                            if (failure) {
                            if (failure) {
                                failure(request, response, error);
                                failure(request, response, error);
                            }
                            }

+ 12 - 6
UIKit+AFNetworking/UIImageView+AFNetworking.m

@@ -103,35 +103,37 @@
         } else {
         } else {
             self.image = cachedImage;
             self.image = cachedImage;
         }
         }
-        self.af_activeImageDownloadReceipt = nil;
+        [self clearActiveDownloadInformation];
     } else {
     } else {
         if (placeholderImage) {
         if (placeholderImage) {
             self.image = placeholderImage;
             self.image = placeholderImage;
         }
         }
 
 
         __weak __typeof(self)weakSelf = self;
         __weak __typeof(self)weakSelf = self;
+        NSUUID *downloadID = [NSUUID UUID];
         AFImageDownloadReceipt *receipt;
         AFImageDownloadReceipt *receipt;
         receipt = [downloader
         receipt = [downloader
                    downloadImageForURLRequest:urlRequest
                    downloadImageForURLRequest:urlRequest
+                   withReceiptID:downloadID
                    success:^(NSURLRequest * _Nonnull request, NSHTTPURLResponse * _Nullable response, UIImage * _Nonnull responseObject) {
                    success:^(NSURLRequest * _Nonnull request, NSHTTPURLResponse * _Nullable response, UIImage * _Nonnull responseObject) {
                        __strong __typeof(weakSelf)strongSelf = weakSelf;
                        __strong __typeof(weakSelf)strongSelf = weakSelf;
-                       if ([strongSelf isActiveTaskURLEqualToURLRequest:request]) {
+                       if ([strongSelf.af_activeImageDownloadReceipt.receiptID isEqual:downloadID]) {
                            if (success) {
                            if (success) {
                                success(request, response, responseObject);
                                success(request, response, responseObject);
                            } else if(responseObject) {
                            } else if(responseObject) {
                                strongSelf.image = responseObject;
                                strongSelf.image = responseObject;
                            }
                            }
-                           strongSelf.af_activeImageDownloadReceipt = nil;
+                           [strongSelf clearActiveDownloadInformation];
                        }
                        }
 
 
                    }
                    }
                    failure:^(NSURLRequest * _Nonnull request, NSHTTPURLResponse * _Nullable response, NSError * _Nonnull error) {
                    failure:^(NSURLRequest * _Nonnull request, NSHTTPURLResponse * _Nullable response, NSError * _Nonnull error) {
                        __strong __typeof(weakSelf)strongSelf = weakSelf;
                        __strong __typeof(weakSelf)strongSelf = weakSelf;
-                        if ([strongSelf isActiveTaskURLEqualToURLRequest:request]) {
+                        if ([strongSelf.af_activeImageDownloadReceipt.receiptID isEqual:downloadID]) {
                             if (failure) {
                             if (failure) {
                                 failure(request, response, error);
                                 failure(request, response, error);
                             }
                             }
-                            strongSelf.af_activeImageDownloadReceipt = nil;
+                            [strongSelf clearActiveDownloadInformation];
                         }
                         }
                    }];
                    }];
 
 
@@ -142,10 +144,14 @@
 - (void)cancelImageDownloadTask {
 - (void)cancelImageDownloadTask {
     if (self.af_activeImageDownloadReceipt != nil) {
     if (self.af_activeImageDownloadReceipt != nil) {
         [[self.class sharedImageDownloader] cancelTaskForImageDownloadReceipt:self.af_activeImageDownloadReceipt];
         [[self.class sharedImageDownloader] cancelTaskForImageDownloadReceipt:self.af_activeImageDownloadReceipt];
-        self.af_activeImageDownloadReceipt = nil;
+        [self clearActiveDownloadInformation];
      }
      }
 }
 }
 
 
+- (void)clearActiveDownloadInformation {
+    self.af_activeImageDownloadReceipt = nil;
+}
+
 - (BOOL)isActiveTaskURLEqualToURLRequest:(NSURLRequest *)urlRequest {
 - (BOOL)isActiveTaskURLEqualToURLRequest:(NSURLRequest *)urlRequest {
     return [self.af_activeImageDownloadReceipt.task.originalRequest.URL.absoluteString isEqualToString:urlRequest.URL.absoluteString];
     return [self.af_activeImageDownloadReceipt.task.originalRequest.URL.absoluteString isEqualToString:urlRequest.URL.absoluteString];
 }
 }