源码阅读:AFNetworking(十三)——UIImageView+AFNetworking

657 阅读4分钟

该文章阅读的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