版本記錄
版本號(hào) | 時(shí)間 |
---|---|
V1.0 | 2018.03.05 |
前言
我們做APP發(fā)起網(wǎng)絡(luò)請(qǐng)求,都離不開一個(gè)非常有用的框架AFNetworking腥例,可以說這個(gè)框架的知名度已經(jīng)超過了蘋果的底層網(wǎng)絡(luò)請(qǐng)求部分,很多人可能不知道蘋果底層是如何發(fā)起網(wǎng)絡(luò)請(qǐng)求的,但是一定知道
AFNetworking
铐炫,接下來幾篇我們就一起詳細(xì)的解析一下這個(gè)框架黎烈。感興趣的可以看上面寫的幾篇习柠。
1. AFNetworking源碼探究(一) —— 基本介紹
2. AFNetworking源碼探究(二) —— GET請(qǐng)求實(shí)現(xiàn)之NSURLSessionDataTask實(shí)例化(一)
3. AFNetworking源碼探究(三) —— GET請(qǐng)求實(shí)現(xiàn)之任務(wù)進(jìn)度設(shè)置和通知監(jiān)聽(一)
4. AFNetworking源碼探究(四) —— GET請(qǐng)求實(shí)現(xiàn)之代理轉(zhuǎn)發(fā)思想(一)
5. AFNetworking源碼探究(五) —— AFURLSessionManager中NSURLSessionDelegate詳細(xì)解析(一)
6. AFNetworking源碼探究(六) —— AFURLSessionManager中NSURLSessionTaskDelegate詳細(xì)解析(一)
7. AFNetworking源碼探究(七) —— AFURLSessionManager中NSURLSessionDataDelegate詳細(xì)解析(一)
8. AFNetworking源碼探究(八) —— AFURLSessionManager中NSURLSessionDownloadDelegate詳細(xì)解析(一)
9. AFNetworking源碼探究(九) —— AFURLSessionManagerTaskDelegate中三個(gè)轉(zhuǎn)發(fā)代理方法詳細(xì)解析(一)
10. AFNetworking源碼探究(十) —— 數(shù)據(jù)解析之?dāng)?shù)據(jù)解析架構(gòu)的分析(一)
11. AFNetworking源碼探究(十一) —— 數(shù)據(jù)解析之子類中協(xié)議方法的實(shí)現(xiàn)(二)
12. AFNetworking源碼探究(十二) —— 數(shù)據(jù)解析之子類中協(xié)議方法的實(shí)現(xiàn)(三)
13. AFNetworking源碼探究(十三) —— AFSecurityPolicy與安全認(rèn)證 (一)
14. AFNetworking源碼探究(十四) —— AFSecurityPolicy與安全認(rèn)證 (二)
15. AFNetworking源碼探究(十五) —— 請(qǐng)求序列化之架構(gòu)分析(一)
16. AFNetworking源碼探究(十六) —— 請(qǐng)求序列化之協(xié)議方法的實(shí)現(xiàn)(二)
17. AFNetworking源碼探究(十七) —— _AFURLSessionTaskSwizzling實(shí)現(xiàn)方法交換(轉(zhuǎn)載)(一)
18. AFNetworking源碼探究(十八) —— UIKit相關(guān)之AFNetworkActivityIndicatorManager(一)
19. AFNetworking源碼探究(十九) —— UIKit相關(guān)之幾個(gè)分類(二)
20. AFNetworking源碼探究(二十) —— UIKit相關(guān)之AFImageDownloader圖像下載(三)
回顧
上一篇是關(guān)于AFImageDownloader圖像下載的內(nèi)容,這一篇主要是關(guān)于UIImageView的分類AFNetworking
照棋。
AFNetworking類
先看一下UIImageView的分類AFNetworking的接口资溃。
#import <Foundation/Foundation.h>
#import <TargetConditionals.h>
#if TARGET_OS_IOS || TARGET_OS_TV
#import <UIKit/UIKit.h>
NS_ASSUME_NONNULL_BEGIN
@class AFImageDownloader;
/**
This category adds methods to the UIKit framework's `UIImageView` class. The methods in this category provide support for loading remote images asynchronously from a URL.
*/
// 此類為UIKit框架的UIImageView類添加方法。 此類別中的方法支持從URL異步加載遠(yuǎn)程圖像烈炭。
@interface UIImageView (AFNetworking)
///------------------------------------
/// @name Accessing the Image Downloader
///------------------------------------
/**
Set the shared image downloader used to download images.
@param imageDownloader The shared image downloader used to download images.
*/
+ (void)setSharedImageDownloader:(AFImageDownloader *)imageDownloader;
/**
The shared image downloader used to download images.
*/
+ (AFImageDownloader *)sharedImageDownloader;
///--------------------
/// @name Setting Image
///--------------------
/**
Asynchronously downloads an image from the specified URL, and sets it once the request is finished. Any previous image request for the receiver will be cancelled.
If the image is cached locally, the image is set immediately, otherwise the specified placeholder image will be set immediately, and then the remote image will be set once the request is finished.
By default, URL requests have a `Accept` header field value of "image / *", a cache policy of `NSURLCacheStorageAllowed` and a timeout interval of 30 seconds, and are set not handle cookies. To configure URL requests differently, use `setImageWithURLRequest:placeholderImage:success:failure:`
@param url The URL used for the image request.
*/
// 異步地從指定的URL下載圖像溶锭,并在請(qǐng)求完成后進(jìn)行設(shè)置。
// 以前任何接收者的圖像請(qǐng)求都將被取消梳庆。
// 如果圖像在本地緩存暖途,則立即設(shè)置圖像卑惜,否則將立即設(shè)置指定的占位符圖像,
// 然后在請(qǐng)求完成后設(shè)置遠(yuǎn)程圖像驻售。
// 默認(rèn)情況下露久,URL請(qǐng)求的“Accept”標(biāo)頭字段值為“image / *”,緩存策略為“NSURLCacheStorageAllowed”欺栗,
// 超時(shí)間隔為30秒毫痕,并且設(shè)置為不處理cookie。 要以不同的方式配置URL請(qǐng)求迟几,
// 請(qǐng)使用`setImageWithURLRequest:placeholderImage:success:failure:`
- (void)setImageWithURL:(NSURL *)url;
/**
Asynchronously downloads an image from the specified URL, and sets it once the request is finished. Any previous image request for the receiver will be cancelled.
If the image is cached locally, the image is set immediately, otherwise the specified placeholder image will be set immediately, and then the remote image will be set once the request is finished.
By default, URL requests have a `Accept` header field value of "image / *", a cache policy of `NSURLCacheStorageAllowed` and a timeout interval of 30 seconds, and are set not handle cookies. To configure URL requests differently, use `setImageWithURLRequest:placeholderImage:success:failure:`
@param url The URL used for the image request.
@param placeholderImage The image to be set initially, until the image request finishes. If `nil`, the image view will not change its image until the image request finishes.
*/
- (void)setImageWithURL:(NSURL *)url
placeholderImage:(nullable UIImage *)placeholderImage;
/**
Asynchronously downloads an image from the specified URL request, and sets it once the request is finished. Any previous image request for the receiver will be cancelled.
If the image is cached locally, the image is set immediately, otherwise the specified placeholder image will be set immediately, and then the remote image will be set once the request is finished.
If a success block is specified, it is the responsibility of the block to set the image of the image view before returning. If no success block is specified, the default behavior of setting the image with `self.image = image` is applied.
@param urlRequest The URL request used for the image request.
@param placeholderImage The image to be set initially, until the image request finishes. If `nil`, the image view will not change its image until the image request finishes.
@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.
*/
- (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;
/**
Cancels any executing image operation for the receiver, if one exists.
*/
- (void)cancelImageDownloadTask;
@end
NS_ASSUME_NONNULL_END
#endif
怎么樣消请,大家有沒有很熟悉,沒錯(cuò)类腮,這個(gè)和SDWebImage非常類似臊泰,包括接口的調(diào)用和實(shí)現(xiàn)。
runtime綁定
這里采用runtime進(jìn)行綁定獲取分類的對(duì)象蚜枢。
+ (AFImageDownloader *)sharedImageDownloader {
return objc_getAssociatedObject(self, @selector(sharedImageDownloader)) ?: [AFImageDownloader defaultInstance];
}
+ (void)setSharedImageDownloader:(AFImageDownloader *)imageDownloader {
objc_setAssociatedObject(self, @selector(sharedImageDownloader), imageDownloader, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
然后調(diào)用AFImageDownloader
獲取下載器的實(shí)例化對(duì)象缸逃。
下載接口的調(diào)用
下面看一下下載圖像的接口調(diào)用。
- (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;
這個(gè)接口和SDWebImage非常類似厂抽,實(shí)現(xiàn)也差不多這樣需频。
下載接口的實(shí)現(xiàn)
下面就看一下下載接口的實(shí)現(xiàn)。
- (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;
//Use the image from the image cache if it exists
UIImage *cachedImage = [imageCache imageforRequest:urlRequest withAdditionalIdentifier:nil];
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 = [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]) {
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]) {
if (failure) {
failure(request, response, error);
}
[strongSelf clearActiveDownloadInformation];
}
}];
self.af_activeImageDownloadReceipt = receipt;
}
}
這個(gè)類主要做下面幾個(gè)工作:
1. 容錯(cuò)處理
if ([urlRequest URL] == nil) {
[self cancelImageDownloadTask];
self.image = placeholderImage;
return;
}
這里如果request的URL不存在的話筷凤,那就無法請(qǐng)求了昭殉,這里就將當(dāng)前UIImageView的image設(shè)置為palceHolder圖像,并取消該圖像下載任務(wù)藐守。
- (void)cancelImageDownloadTask {
if (self.af_activeImageDownloadReceipt != nil) {
[[self.class sharedImageDownloader] cancelTaskForImageDownloadReceipt:self.af_activeImageDownloadReceipt];
[self clearActiveDownloadInformation];
}
}
這里就是圖像下載任務(wù)取消的實(shí)現(xiàn):
- 首先就是判斷下載任務(wù)的憑據(jù)是否存在挪丢,如果不存在不用管,說明沒有這個(gè)任務(wù)吗伤,這里只處理有這個(gè)任務(wù)的情況吃靠。
- 調(diào)用下載器的
cancelTaskForImageDownloadReceipt:
方法,帶人憑據(jù)參數(shù)足淆,取消下載任務(wù)巢块。 - 初始化憑據(jù)參數(shù),置為nil巧号,實(shí)現(xiàn)過程如下族奢。
- (void)clearActiveDownloadInformation {
self.af_activeImageDownloadReceipt = nil;
}
2. 存在任務(wù)的判斷
下面就看一下根據(jù)URL判斷任務(wù)是否存在,如果存在就return丹鸿,接著就是取消圖像下載任務(wù)越走。
if ([self isActiveTaskURLEqualToURLRequest:urlRequest]){
return;
}
[self cancelImageDownloadTask];
- (BOOL)isActiveTaskURLEqualToURLRequest:(NSURLRequest *)urlRequest {
return [self.af_activeImageDownloadReceipt.task.originalRequest.URL.absoluteString isEqualToString:urlRequest.URL.absoluteString];
}
3. 獲取緩存圖像并做決策
首先看一下這段代碼
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];
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 = [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]) {
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]) {
if (failure) {
failure(request, response, error);
}
[strongSelf clearActiveDownloadInformation];
}
}];
self.af_activeImageDownloadReceipt = receipt;
}
這段代碼,主要完成下面幾個(gè)工作:
- 獲取緩存圖像
- 如果存在,就進(jìn)行回調(diào)和設(shè)置圖像
- 不存在就開始下載圖像
(a) 獲取緩存圖像
我們首先看一下緩存圖像廊敌。
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];
這里铜跑,首先是獲取下載器,然后獲取下載器的緩存骡澈,最后根據(jù)請(qǐng)求request獲取UIImage锅纺。
(b) 緩存圖像存在
主要對(duì)應(yīng)下面這段代碼
if (cachedImage) {
if (success) {
success(urlRequest, nil, cachedImage);
} else {
self.image = cachedImage;
}
[self clearActiveDownloadInformation];
}
如果緩存圖像存在,如果success的block存在就回調(diào)出去肋殴,否則就賦值給image囤锉。最后還是調(diào)用clearActiveDownloadInformation
,清除下載信息护锤。
(c) 緩存圖像不存在
其實(shí)主要對(duì)應(yīng)下面這段代碼
else {
if (placeholderImage) {
self.image = placeholderImage;
}
__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]) {
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]) {
if (failure) {
failure(request, response, error);
}
[strongSelf clearActiveDownloadInformation];
}
}];
self.af_activeImageDownloadReceipt = receipt;
}
緩存圖像不存在官地,那就是要開始下載了。主要步驟如下:
- 暫時(shí)將圖像設(shè)置為占位符烙懦。
- 根據(jù)下載器返回的憑據(jù)驱入,更新內(nèi)存中的有效憑據(jù)
self.af_activeImageDownloadReceipt
。 - 用下載器進(jìn)行下載氯析,不管成功還是失敗都進(jìn)行相應(yīng)的回調(diào)沧侥,并清除下載信息
clearActiveDownloadInformation
。并在成功的時(shí)候設(shè)置圖像替換掉下載圖strongSelf.image = responseObject
魄鸦。
后記
本篇講述了關(guān)于UIImageView的分類,用于下載圖像癣朗。