原始碼閱讀:AFNetworking(十三)——UIImageView+AFNetworking

堯少羽發表於2018-03-16

該文章閱讀的AFNetworking的版本為3.2.0。

這個分類是為UIImageView新增非同步載入網路圖片的方法

1.介面方法

  • 圖片下載器的訪問方法
/**
 設定用於下載圖片的圖片下載器
 */
+ (void)setSharedImageDownloader:(AFImageDownloader *)imageDownloader;

/**
 獲取用於下載圖片的圖片下載器
 */
+ (AFImageDownloader *)sharedImageDownloader;
複製程式碼
  • 為圖片檢視設定圖片的方法
/**
 為圖片檢視設定指定圖片連結的圖片
 */
- (void)setImageWithURL:(NSURL *)url;

/**
 為圖片檢視設定指定圖片連結的圖片,以及指定佔點陣圖
 */
- (void)setImageWithURL:(NSURL *)url
       placeholderImage:(nullable UIImage *)placeholderImage;

/**
 為圖片檢視設定指定圖片連結、指定佔點陣圖以及指定成功與失敗回撥block的圖片
 */
- (void)setImageWithURLRequest:(NSURLRequest *)urlRequest
              placeholderImage:(nullable UIImage *)placeholderImage
                       success:(nullable void (^)(NSURLRequest *request, NSHTTPURLResponse * _Nullable response, UIImage *image))success
                       failure:(nullable void (^)(NSURLRequest *request, NSHTTPURLResponse * _Nullable response, NSError *error))failure;

/**
 取消當前圖片下載任務
 */
- (void)cancelImageDownloadTask;
複製程式碼

2.UIImageView+_AFNetworking私有分類

2.1.介面

/**
 用於儲存下載封裝物件
 */
@property (readwrite, nonatomic, strong, setter = af_setActiveImageDownloadReceipt:) AFImageDownloadReceipt *af_activeImageDownloadReceipt;
複製程式碼

2.2.實現

實現裡的兩個方法就是通過Runtime的關聯物件為分類新增屬性儲存下載封裝物件

- (AFImageDownloadReceipt *)af_activeImageDownloadReceipt {
    return (AFImageDownloadReceipt *)objc_getAssociatedObject(self, @selector(af_activeImageDownloadReceipt));
}

- (void)af_setActiveImageDownloadReceipt:(AFImageDownloadReceipt *)imageDownloadReceipt {
    objc_setAssociatedObject(self, @selector(af_activeImageDownloadReceipt), imageDownloadReceipt, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
複製程式碼

3.方法實現

  • 屬性的訪問方法

這兩個方法同樣是通過關聯物件為分類新增屬性儲存圖片下載物件

+ (AFImageDownloader *)sharedImageDownloader {
    return objc_getAssociatedObject(self, @selector(sharedImageDownloader)) ?: [AFImageDownloader defaultInstance];
}

+ (void)setSharedImageDownloader:(AFImageDownloader *)imageDownloader {
    objc_setAssociatedObject(self, @selector(sharedImageDownloader), imageDownloader, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
複製程式碼
  • 設定圖片的方法
- (void)setImageWithURL:(NSURL *)url {
    // 呼叫下面的方法
    [self setImageWithURL:url placeholderImage:nil];
}

- (void)setImageWithURL:(NSURL *)url
       placeholderImage:(UIImage *)placeholderImage
{
    // 通過圖片連結生成請求物件,然後呼叫下面的方法
    NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
    [request addValue:@"image/*" forHTTPHeaderField:@"Accept"];

    [self setImageWithURLRequest:request placeholderImage:placeholderImage success:nil failure:nil];
}

- (void)setImageWithURLRequest:(NSURLRequest *)urlRequest
              placeholderImage:(UIImage *)placeholderImage
                       success:(void (^)(NSURLRequest *request, NSHTTPURLResponse * _Nullable response, UIImage *image))success
                       failure:(void (^)(NSURLRequest *request, NSHTTPURLResponse * _Nullable response, NSError *error))failure
{
    
    // 如果請求物件的連結為空,就取消掉之前的圖片下載任務,展示佔點陣圖,返回結束
    if ([urlRequest URL] == nil) {
        [self cancelImageDownloadTask];
        self.image = placeholderImage;
        return;
    }

    // 如果這個網路請求正在進行中就不重複執行了
    if ([self isActiveTaskURLEqualToURLRequest:urlRequest]){
        return;
    }

    // 取消掉之前的圖片下載任務
    [self cancelImageDownloadTask];

    // 獲取圖片下載器物件
    AFImageDownloader *downloader = [[self class] sharedImageDownloader];
    // 獲取圖片下載器物件中的圖片快取物件
    id <AFImageRequestCache> imageCache = downloader.imageCache;

    // 從圖片快取物件中獲取快取圖片
    UIImage *cachedImage = [imageCache imageforRequest:urlRequest withAdditionalIdentifier:nil];
    // 如果有快取圖片
    if (cachedImage) {
        // 如果設定了成功回撥block就呼叫block,否則就直接設定圖片
        if (success) {
            success(urlRequest, nil, cachedImage);
        } else {
            self.image = cachedImage;
        }
        [self clearActiveDownloadInformation];
    // 如果沒有快取圖片
    } else {
        // 如果設定了佔點陣圖就展示佔點陣圖
        if (placeholderImage) {
            self.image = placeholderImage;
        }

        // 獲取UUID,生成下載封裝物件,並開始下載
        __weak __typeof(self)weakSelf = self;
        NSUUID *downloadID = [NSUUID UUID];
        AFImageDownloadReceipt *receipt;
        receipt = [downloader
                   downloadImageForURLRequest:urlRequest
                   withReceiptID:downloadID
                   success:^(NSURLRequest * _Nonnull request, NSHTTPURLResponse * _Nullable response, UIImage * _Nonnull responseObject) {
                       __strong __typeof(weakSelf)strongSelf = weakSelf;
                       // 如果是當前控制元件發起的任務
                       if ([strongSelf.af_activeImageDownloadReceipt.receiptID isEqual:downloadID]) {
                           // 如果設定了成功回撥block就呼叫block,否則就直接設定圖片
                           if (success) {
                               success(request, response, responseObject);
                           } else if(responseObject) {
                               strongSelf.image = responseObject;
                           }
                           // 清除該控制元件的下載任務資訊
                           [strongSelf clearActiveDownloadInformation];
                       }

                   }
                   failure:^(NSURLRequest * _Nonnull request, NSHTTPURLResponse * _Nullable response, NSError * _Nonnull error) {
                       __strong __typeof(weakSelf)strongSelf = weakSelf;
                        // 如果是當前控制元件發起的任務
                        if ([strongSelf.af_activeImageDownloadReceipt.receiptID isEqual:downloadID]) {
                            // 如果設定了失敗回撥block就呼叫block
                            if (failure) {
                                failure(request, response, error);
                            }
                            // 清除該控制元件的下載任務資訊
                            [strongSelf clearActiveDownloadInformation];
                        }
                   }];

        // 使用屬性儲存生成的下載封裝物件
        self.af_activeImageDownloadReceipt = receipt;
    }
}

- (void)cancelImageDownloadTask {
    // 如果當前有下載任務
    if (self.af_activeImageDownloadReceipt != nil) {
        // 通過任務封裝物件將下載任務取消
        [[self.class sharedImageDownloader] cancelTaskForImageDownloadReceipt:self.af_activeImageDownloadReceipt];
        // 清除該控制元件的下載任務資訊
        [self clearActiveDownloadInformation];
     }
}

- (void)clearActiveDownloadInformation {
    // 將儲存下載封裝物件的屬性置空
    self.af_activeImageDownloadReceipt = nil;
}

- (BOOL)isActiveTaskURLEqualToURLRequest:(NSURLRequest *)urlRequest {
    // 判斷屬性中儲存的下載封裝物件的下載連結和傳入的請求物件下載連結是否相同
    return [self.af_activeImageDownloadReceipt.task.originalRequest.URL.absoluteString isEqualToString:urlRequest.URL.absoluteString];
}
複製程式碼

4.總結

可以看到為UIImageView設定圖片的邏輯和為UIButton設定圖片的邏輯基本相同,不太一樣的是設定UIButton圖片的時候,還會根據設定的據控制元件的狀態進行區分和辨別。

原始碼閱讀系列:AFNetworking

原始碼閱讀:AFNetworking(一)——從使用入手

原始碼閱讀:AFNetworking(二)——AFURLRequestSerialization

原始碼閱讀:AFNetworking(三)——AFURLResponseSerialization

原始碼閱讀:AFNetworking(四)——AFSecurityPolicy

原始碼閱讀:AFNetworking(五)——AFNetworkReachabilityManager

原始碼閱讀:AFNetworking(六)——AFURLSessionManager

原始碼閱讀:AFNetworking(七)——AFHTTPSessionManager

原始碼閱讀:AFNetworking(八)——AFAutoPurgingImageCache

原始碼閱讀:AFNetworking(九)——AFImageDownloader

原始碼閱讀:AFNetworking(十)——AFNetworkActivityIndicatorManager

原始碼閱讀:AFNetworking(十一)——UIActivityIndicatorView+AFNetworking

原始碼閱讀:AFNetworking(十二)——UIButton+AFNetworking

原始碼閱讀:AFNetworking(十三)——UIImageView+AFNetworking

原始碼閱讀:AFNetworking(十四)——UIProgressView+AFNetworking

原始碼閱讀:AFNetworking(十五)——UIRefreshControl+AFNetworking

原始碼閱讀:AFNetworking(十六)——UIWebView+AFNetworking

相關文章