网络请求

UIKit+AFNetworking源码

2017-07-07  本文已影响91人  继续向前冲

之前写了AFNetworking说啦要写一篇UIKit+AFNetworking,趁热打铁赶紧行动

项目目录
AFN很贴心的为我们带来了很多辅助功能,还是很强大的,先挑最主要的讲起。首先UIImageView+AFNetworking,他这个功能类似于sdwebimage的图片缓存功能,一共使用UIImageView+AFNetworkingAFImageDownloaderAFAutoPurgingImageCache三个类实现这个工,一共是对用户封装的功能,主要异步调用网络加载图片,并且缓存图片不会造成重复加载
- (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;

(ps:跟sdwebimage无限撞脸啊,是不是看了一个后就不需要看其他的了,还是他俩相互抄啊)。AFImageDownloader则是负责利用AFN强大的网络工进行下载,并且按照其中的图片格式进行解析,AFAutoPurgingImageCache是为了对图片进行缓存。可以有效预防重复加载,减少网络的请求。下面先从核心的下载原理一点点的讲解,循序渐进。

AFImageDownloader

首先还是先看初始化,init中一般都有很大的信息量

init

首先初始化了一个配置了网络,如果想改默认配置的请改此处,注释已经标明。

配置网络

设置缓存对象的时候又进行展开啦

NSURLCache配置

后来AFN也封装了一个,因为系统默认的由系统接管,外界不好控制。

配置下载任务

初始化一个NSURLSession 的下载任务以便下载使用
下面讲的是下载的核心方法


- (nullable AFImageDownloadReceipt *)downloadImageForURLRequest:(NSURLRequest *)request
                                                  withReceiptID:(nonnull NSUUID *)receiptID
                                                        success:(nullable void (^)(NSURLRequest *request, NSHTTPURLResponse  * _Nullable response, UIImage *responseObject))success
                                                        failure:(nullable void (^)(NSURLRequest *request, NSHTTPURLResponse * _Nullable response, NSError *error))failure

此类分为好多步骤,无奈家里没有显示器,截图比较费劲,只能分开截图了

下载图片1

首先同步执行一个串行队列,这个在init的时候分别申请了一个串行队列,一个并行队列,这是要搞事情啊,这根图片的缓存机制有关,下载的时候是串行的,但是缓存或者展示的时候就是异步的了,因为无论是渲染图片还是把图片存本地都需要时间嘛,下载的时候同步是防止出差啊。(感觉又说多啦)。首先判断URL是否为空,为空还搞毛。
mergedTasks为一个字典把创建了了的task存储起来,此时从字典里如果不为空,则证明此任务已经创建过了,AFImageDownloaderMergedTask 也只一个NSURLSessionDataTask的简单封装,自己加了一个id和URL一边区分任务。AFImageDownloaderMergedTask中还有一个

@property (nonatomic, strong) NSMutableArray <AFImageDownloaderResponseHandler*> *responseHandlers;

这也是一个简单封装,因为一个任务中可能会有不同的结果,把结果也一起封禁去。
言归正传,判断出如果task已经存在就把当前的成功或者失败的结果存到刚刚提到的属猪里,并且给现在task赋值,不会重新创建task避免重复下载。

读取缓存

判断请求的缓存策略,在如图的三种策略是都会充缓存中进行加载数据。如果读取缓存一会再讲。

开始请求1
开始请求2

先取出来一个UUID 当ID标识task,然后执行afn封装的下载方法,然后创建线程,展示图片,创建一个mergedTask然后给它简单到mergedTasks中,判断如果小于最大缓存数,这立刻执行任务,否则按照之前选择策略FIFO LIFO 执行任务,创建一个由ID和task注册的类里,标识task。根据执行完网络的回调,此时从字典里取出task如果task的identifier和现在的ID相等,开一个同步任务从任务字典里把此task删除,如果报错的话就返回失败的block 如果成功的话,就往缓存里添加值,并且返回成功的block,这一切做完后再更新当前执行任务的数量以及让排队的task继续执行。下面就讲解依稀缓存类

AFAutoPurgingImageCache

首先初始化了最大内存数量,以及最佳缓存数量。然后初始化一个并行队列,还有监听清内存警告。下面介绍一个核心方法

- (void)addImage:(UIImage *)image withIdentifier:(NSString *)identifier
屏幕快照 2017-07-07 下午10.26.21.png

里面用到了dispatch_barrier_async,请自行Google啦,也就是虽然是ansync 但是还是同步执行,首先初始化一个AFCachedImage 其实这货就是一个个image和统计数量的结构体,维护用一下数量,然后把新的缓存加进去,然后清除无用缓存。其他的方法是就是维护这个字典,为了保护线程安全,基本上所有的操作都是同步执行的。

UIImageView+AFNetworking

如果前面两个都理解了,那这个类就很好理解了,其实就是功能的集合

- (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
{
     //url为空,则取消
    if ([urlRequest URL] == nil) {
        //取消task
        [self cancelImageDownloadTask];
        //设置为占位图
        self.image = placeholderImage;
        return;
    }
 //看看设置的当前的回调的request和需要请求的request是不是为同一个,是的话为重复调用,直接返回
    if ([self isActiveTaskURLEqualToURLRequest:urlRequest]){
        return;
    }

    [self cancelImageDownloadTask];

    AFImageDownloader *downloader = [[self class] sharedImageDownloader];
    id <AFImageRequestCache> imageCache = downloader.imageCache;

    //Use the image from the image cache if it exists
    UIImage *cachedImage = [imageCache imageforRequest:urlRequest withAdditionalIdentifier:nil];
     //去获取cachedImage
    if (cachedImage) {
        if (success) {
            success(urlRequest, nil, cachedImage);
        } else {
            self.image = cachedImage;
        }
        [self clearActiveDownloadInformation];
    } else {
        if (placeholderImage) {//设置占位图片
            self.image = placeholderImage;
        }

        __weak __typeof(self)weakSelf = self;
        NSUUID *downloadID = [NSUUID UUID];
        AFImageDownloadReceipt *receipt;
        //去下载,并得到一个receipt,可以用来取消回调
        receipt = [downloader
                   downloadImageForURLRequest:urlRequest
                   withReceiptID:downloadID
                   success:^(NSURLRequest * _Nonnull request, NSHTTPURLResponse * _Nullable response, UIImage * _Nonnull responseObject) {
                       __strong __typeof(weakSelf)strongSelf = weakSelf;
                       //判断receiptID和downloadID是否相同 成功回调,设置图片
                       if ([strongSelf.af_activeImageDownloadReceipt.receiptID isEqual:downloadID]) {
                           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;
                             //失败有failuerBlock就回调,
                        if ([strongSelf.af_activeImageDownloadReceipt.receiptID isEqual:downloadID]) {
                            if (failure) {
                                failure(request, response, error);
                            }
                               //置空回调对象
                            [strongSelf clearActiveDownloadInformation];
                        }
                   }];

        self.af_activeImageDownloadReceipt = receipt;
    }
}

先判断URL 合法性,然后判断请求是否同一个,如果是就返回了,在之后先取消下载,然后从缓存中去,如果有的话就返回,没有的话先设置占位图片再去网上下载,如果合法返回不和把取消请求。

上一篇下一篇

猜你喜欢

热点阅读