源码分析之AFNetworking④UIkit+AFNetworking

Posted 梦想家-mxj

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了源码分析之AFNetworking④UIkit+AFNetworking相关的知识,希望对你有一定的参考价值。

如果需要使用AFNetworking的UIKit扩展时可直接在prefix.pch文件中引入,或者在工程的相关文件中引入。

1、AFAutoPurgingImageCache :用于缓存图片的类,通过identifier来添加和搜索UIImage
协议中添加图片:

- (void)addImage:(UIImage*)image withIdentifier:(NSString *)identifier;
协议中删除图片
- (BOOL)removeImageWithIdentifier:(NSString*)identifier;
通过identifier获取图片的方法:
- (nullable UIImage*)imageWithIdentifer:(NSString *)identifier;

2、UIButton+AFNetworking
UIButton跟图片相关的属性大概有两个,Image和BackgroundImage,所以这个分类就是赋予他们异步加载图片的能力。
核心的方法如下:

在这里插入图片描述

- (void)setImageForState:(UIControlState)state
                 withURL:(NSURL *)url;

异步下载指定url的图片,并且一旦请求完成就将其设置为某种状态的图片。如果图片已经被缓存在本地了,那么就取本地的图片、或者显示指定的占位图,等远程图片下载完成就立即替换。

在这里插入图片描述

- (void)setImageForState:(UIControlState)state
                 withURL:(NSURL *)url
        placeholderImage:(nullable UIImage *)placeholderImage;

和①一样都是异步下载指定url的图片


在这里插入图片描述

- (void)setImageForState:(UIControlState)state
          withURLRequest:(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)setBackgroundImageForState:(UIControlState)state
                           withURL:(NSURL *)url;

异步下载指定url的图片,并将图片设置为某种状态的按钮的背景图片

- (void)setBackgroundImageForState:(UIControlState)state
                           withURL:(NSURL *)url
                  placeholderImage:(nullable UIImage *)placeholderImage;

比④多了占位的背景图

- (void)setBackgroundImageForState:(UIControlState)state
                    withURLRequest:(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)cancelImageDownloadTaskForState:(UIControlState)state;

- (void)cancelBackgroundImageDownloadTaskForState:(UIControlState)state;

取消下载操作
3、UIImageView+AFNetworking
UIImageView+AFNetworking 是AFNetworking中一个实现图片异步加载的类,它是为系统中的UIImageView类添加的分类,这个分类中的方法未远程异步加载图片的功能提供支持。

- (void)setImageWithURL:(NSURL *)url;

- (void)setImageWithURL:(NSURL *)url
       placeholderImage:(nullable UIImage *)placeholderImage;
       
- (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;

上面三个方法都是UIImageView异步下载指定url的图片。首先会检查本地有没有缓存图片,如果有就返回,没有就去下载;如果有占位图,就显示展位图,然后下载远程图片,替换展位图。

上面的三个方法最终会调用
AFImageDownloader的对象方法:

- (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;

在这个方法内部会将下载的图片缓存起来。

4、WKWebView+AFNetworking
WKWebView+AFNetworking这个是WKwebview的扩展类,里面有两个方法

在这里插入图片描述

- (void)loadRequest:(NSURLRequest *)request
         navigation:(WKNavigation * _Nonnull)navigation
           progress:(NSProgress * _Nullable __autoreleasing * _Nullable)progress
            success:(nullable NSString * (^)(NSHTTPURLResponse *response, NSString *html))success
            failure:(nullable void (^)(NSError *error))failure;

异步加载请求
request 一个标识加载内容位置的url 请求,不可为nil
navigation Wk的导航,不可为nil
progress 加载进度
success 成功回调
failure 失败回调

在这里插入图片描述

- (void)loadRequest:(NSURLRequest *)request
         navigation:(WKNavigation * _Nonnull)navigation
           MIMEType:(nullable NSString *)MIMEType
   textEncodingName:(nullable NSString *)textEncodingName
           progress:(NSProgress * _Nullable __autoreleasing * _Nullable)progress
            success:(nullable NSData * (^)(NSHTTPURLResponse *response, NSData *data))success
            failure:(nullable void (^)(NSError *error))failure;

查看方法内部实现,①最终会调用②的方法

5、UIProgressView+AFNetworking

为UIProgressView控件绑定task方法,从而获取task的上传下载进度。
调用的API
为指定的上传任务绑定进度控件

- (void)setProgressWithUploadProgressOfTask:(NSURLSessionUploadTask *)task
   animated:(BOOL)animated;

为指定的下载任务绑定进度控件

- (void)setProgressWithDownloadProgressOfTask:(NSURLSessionDownloadTask *)task
       animated:(BOOL)animated;

查看里面的源码可以看到:

- (BOOL)af_uploadProgressAnimated {
    return [(NSNumber *)objc_getAssociatedObject(self, @selector(af_uploadProgressAnimated)) boolValue];
}

- (void)af_setUploadProgressAnimated:(BOOL)animated {
    objc_setAssociatedObject(self, @selector(af_uploadProgressAnimated), @(animated), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}

- (BOOL)af_downloadProgressAnimated {
    return [(NSNumber *)objc_getAssociatedObject(self, @selector(af_downloadProgressAnimated)) boolValue];
}

- (void)af_setDownloadProgressAnimated:(BOOL)animated {
    objc_setAssociatedObject(self, @selector(af_downloadProgressAnimated), @(animated), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}

这四个方法,通过runtime管理的对象为分类添加属性保存进度条的动画选项。

在上传绑定的实现里

- (void)setProgressWithUploadProgressOfTask:(NSURLSessionUploadTask *)task
      animated:(BOOL)animated
{
    if (task.state == NSURLSessionTaskStateCompleted) {
        return;
    }
    
    [task addObserver:self forKeyPath:@"state" options:(NSKeyValueObservingOptions)0 context:AFTaskCountOfBytesSentContext];
    [task addObserver:self forKeyPath:@"countOfBytesSent" options:(NSKeyValueObservingOptions)0 context:AFTaskCountOfBytesSentContext];

    [self af_setUploadProgressAnimated:animated];
}

通过KVO观察task的state和countOfBytesSent属性
下载绑定的实现里

- (void)setProgressWithDownloadProgressOfTask:(NSURLSessionDownloadTask *)task
    animated:(BOOL)animated
{
    if (task.state == NSURLSessionTaskStateCompleted) {
        return;
    }
    
    [task addObserver:self forKeyPath:@"state" options:(NSKeyValueObservingOptions)0 context:AFTaskCountOfBytesReceivedContext];
    [task addObserver:self forKeyPath:@"countOfBytesReceived" options:(NSKeyValueObservingOptions)0 context:AFTaskCountOfBytesReceivedContext];

    [self af_setDownloadProgressAnimated:animated];
}

通过KVO观察task的state和countOfBytesReceived属性
里面有两个静态常量
//任务发送字节数量上下文
static void * AFTaskCountOfBytesSentContext = &AFTaskCountOfBytesSentContext;
//任务接受字节数量上下文
static void * AFTaskCountOfBytesReceivedContext = &AFTaskCountOfBytesReceivedContext;

在KVO的回调中

- (void)observeValueForKeyPath:(NSString *)keyPath
                      ofObject:(id)object
                        change:(__unused NSDictionary *)change
                       context:(void *)context
{
//如果是通过分类添加的观察者(AFTaskCountOfBytesSentContext、AFTaskCountOfBytesReceivedContext)
    if (context == AFTaskCountOfBytesSentContext || context == AFTaskCountOfBytesReceivedContext) {
	//如果task的countOfBytesSent属性发生变化
        if ([keyPath isEqualToString:NSStringFromSelector(@selector(countOfBytesSent))]) {
            if ([object countOfBytesExpectedToSend] > 0) {
                dispatch_async(dispatch_get_main_queue(), ^{//主线程异步调用
			//计算当前的发送进度并未progressview赋值
                    [self setProgress:[object countOfBytesSent] / ([object countOfBytesExpectedToSend] * 1.0f) animated:self.af_uploadProgressAnimated];
                });
            }
        }
	//如果task的countOfBytesReceived属性发生变化
        if ([keyPath isEqualToString:NSStringFromSelector(@selector(countOfBytesReceived))]) {
            if ([object countOfBytesExpectedToReceive] > 0) {//接受的总大小
                dispatch_async(dispatch_get_main_queue(), ^{
			//计算当前进度并未控件赋值
                    [self setProgress:[object countOfBytesReceived] / ([object countOfBytesExpectedToReceive] * 1.0f) animated:self.af_downloadProgressAnimated];
                });
            }
        }
//如果是通过task的state属性发生变化
        if ([keyPath isEqualToString:NSStringFromSelector(@selector(state))]) {
            if ([(NSURLSessionTask *)object state] == NSURLSessionTaskStateCompleted) {
		//完成时,就移除对task的state、 countOfBytesSent、countOfBytesReceived属性的观察
                @try {
                    [object removeObserver:self forKeyPath:NSStringFromSelector(@selector(state))];

                    if (context == AFTaskCountOfBytesSentContext) {
                        [object removeObserver:self forKeyPath:NSStringFromSelector(@selector(countOfBytesSent))];
                    }

                    if (context == AFTaskCountOfBytesReceivedContext) {
                        [object removeObserver:self forKeyPath:NSStringFromSelector(@selector(countOfBytesReceived))];
                    }
                }
                @catch (NSException * __unused exception) {}
            }
        }
    }
}

总的来说,绑定进度条控件主要是通过kvo观察task的属性变化实现的,并在kvo的回到中更新控件进度,在完成后移除对task相关属性的监听。

以上是关于源码分析之AFNetworking④UIkit+AFNetworking的主要内容,如果未能解决你的问题,请参考以下文章

源码分析之AFNetworking④UIkit+AFNetworking

源码分析之AFNetworking④UIkit+AFNetworking

源码分析之AFNetworking ②AFNetworkReachabilityManager

源码分析之AFNetworking ②AFNetworkReachabilityManager

源码分析之AFNetworking ②AFNetworkReachabilityManager

源码分析之AFNetworking③AFSecurityPolicy和AFURLRequestSerialization