該文章閱讀的AFNetworking的版本爲3.2.0。緩存
這個分類是爲UIButton
添加異步加載網絡圖片的方法bash
/**
設置用於下載圖片的圖片下載器
*/
+ (void)setSharedImageDownloader:(AFImageDownloader *)imageDownloader;
/**
獲取用於下載圖片的圖片下載器
*/
+ (AFImageDownloader *)sharedImageDownloader;
複製代碼
/**
爲按鈕設置指定狀態和指定圖片連接的圖片
*/
- (void)setImageForState:(UIControlState)state
withURL:(NSURL *)url;
/**
爲按鈕設置指定狀態、指定圖片連接和指定佔位圖的圖片
*/
- (void)setImageForState:(UIControlState)state
withURL:(NSURL *)url
placeholderImage:(nullable UIImage *)placeholderImage;
/**
爲按鈕設置指定狀態、指定圖片連接、指定佔位圖以及指定成功與失敗回調block的圖片
*/
- (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;
/**
爲按鈕設置指定狀態、指定圖片連接和指定佔位圖的背景圖片
*/
- (void)setBackgroundImageForState:(UIControlState)state
withURL:(NSURL *)url
placeholderImage:(nullable UIImage *)placeholderImage;
/**
爲按鈕設置指定狀態、指定圖片連接、指定佔位圖以及指定成功與失敗回調block的背景圖片
*/
- (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;
複製代碼
// 普通
static char AFImageDownloadReceiptNormal;
// 高亮
static char AFImageDownloadReceiptHighlighted;
// 已選
static char AFImageDownloadReceiptSelected;
// 禁用
static char AFImageDownloadReceiptDisabled;
複製代碼
/**
這個方法經過傳入的控件狀態返回對應的靜態字符
*/
static const char * af_imageDownloadReceiptKeyForState(UIControlState state) {
switch (state) {
case UIControlStateHighlighted:
return &AFImageDownloadReceiptHighlighted;
case UIControlStateSelected:
return &AFImageDownloadReceiptSelected;
case UIControlStateDisabled:
return &AFImageDownloadReceiptDisabled;
case UIControlStateNormal:
default:
return &AFImageDownloadReceiptNormal;
}
}
複製代碼
/**
經過Runtime的關聯對象爲分類添加屬性的getter
*/
- (AFImageDownloadReceipt *)af_imageDownloadReceiptForState:(UIControlState)state {
return (AFImageDownloadReceipt *)objc_getAssociatedObject(self, af_imageDownloadReceiptKeyForState(state));
}
/**
經過Runtime的關聯對象爲分類添加屬性的setter
*/
- (void)af_setImageDownloadReceipt:(AFImageDownloadReceipt *)imageDownloadReceipt
forState:(UIControlState)state
{
objc_setAssociatedObject(self, af_imageDownloadReceiptKeyForState(state), imageDownloadReceipt, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
複製代碼
// 普通
static char AFBackgroundImageDownloadReceiptNormal;
// 高亮
static char AFBackgroundImageDownloadReceiptHighlighted;
// 已選
static char AFBackgroundImageDownloadReceiptSelected;
// 禁用
static char AFBackgroundImageDownloadReceiptDisabled;
複製代碼
/**
這個方法經過傳入的控件狀態返回對應的靜態字符
*/
static const char * af_backgroundImageDownloadReceiptKeyForState(UIControlState state) {
switch (state) {
case UIControlStateHighlighted:
return &AFBackgroundImageDownloadReceiptHighlighted;
case UIControlStateSelected:
return &AFBackgroundImageDownloadReceiptSelected;
case UIControlStateDisabled:
return &AFBackgroundImageDownloadReceiptDisabled;
case UIControlStateNormal:
default:
return &AFBackgroundImageDownloadReceiptNormal;
}
}
複製代碼
/**
經過Runtime的關聯對象爲分類添加屬性的getter
*/
- (AFImageDownloadReceipt *)af_backgroundImageDownloadReceiptForState:(UIControlState)state {
return (AFImageDownloadReceipt *)objc_getAssociatedObject(self, af_backgroundImageDownloadReceiptKeyForState(state));
}
/**
經過Runtime的關聯對象爲分類添加屬性的setter
*/
- (void)af_setBackgroundImageDownloadReceipt:(AFImageDownloadReceipt *)imageDownloadReceipt
forState:(UIControlState)state
{
objc_setAssociatedObject(self, af_backgroundImageDownloadReceiptKeyForState(state), imageDownloadReceipt, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
複製代碼
/**
經過Runtime的關聯對象爲分類添加sharedImageDownloader屬性的getter
*/
+ (AFImageDownloader *)sharedImageDownloader {
return objc_getAssociatedObject(self, @selector(sharedImageDownloader)) ?: [AFImageDownloader defaultInstance];
}
/**
經過Runtime的關聯對象爲分類添加sharedImageDownloader屬性的setter
*/
+ (void)setSharedImageDownloader:(AFImageDownloader *)imageDownloader {
objc_setAssociatedObject(self, @selector(sharedImageDownloader), imageDownloader, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
複製代碼
- (void)setImageForState:(UIControlState)state
withURL:(NSURL *)url
{
// 調用下面的方法
[self setImageForState:state withURL:url placeholderImage:nil];
}
- (void)setImageForState:(UIControlState)state
withURL:(NSURL *)url
placeholderImage:(UIImage *)placeholderImage
{
// 調用下面的方法
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
[request addValue:@"image/*" forHTTPHeaderField:@"Accept"];
[self setImageForState:state withURLRequest:request placeholderImage:placeholderImage success:nil failure:nil];
}
- (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
{
// 若是這個網絡請求正在進行中就不重複執行了
if ([self isActiveTaskURLEqualToURLRequest:urlRequest forState:state]) {
return;
}
// 取消掉這個狀態的圖片下載任務
[self cancelImageDownloadTaskForState:state];
// 實例化圖片下載器
AFImageDownloader *downloader = [[self class] sharedImageDownloader];
// 獲取到圖片緩存對象
id <AFImageRequestCache> imageCache = downloader.imageCache;
// 獲取到緩存圖片
UIImage *cachedImage = [imageCache imageforRequest:urlRequest withAdditionalIdentifier:nil];
// 若是有緩存
if (cachedImage) {
// 若是設置了成功回調block就調用,不然就爲按鈕設置圖片
if (success) {
success(urlRequest, nil, cachedImage);
} else {
[self setImage:cachedImage forState:state];
}
[self af_setImageDownloadReceipt:nil forState:state];
// 若是沒有緩存
} else {
// 若是有佔位圖先設置佔位圖
if (placeholderImage) {
[self setImage:placeholderImage forState:state];
}
// 開始下載
__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_imageDownloadReceiptForState:state].receiptID isEqual:downloadID]) {
// 若是設置了成功回調block就調用,不然就爲按鈕設置圖片
if (success) {
success(request, response, responseObject);
} else if(responseObject) {
[strongSelf setImage:responseObject forState:state];
}
// 將保存下載封裝對象的屬性置nil
[strongSelf af_setImageDownloadReceipt:nil forState:state];
}
}
failure:^(NSURLRequest * _Nonnull request, NSHTTPURLResponse * _Nullable response, NSError * _Nonnull error) {
// 若是是當前按鈕的下載回調
__strong __typeof(weakSelf)strongSelf = weakSelf;
if ([[strongSelf af_imageDownloadReceiptForState:state].receiptID isEqual:downloadID]) {
// 失敗回調block
if (failure) {
failure(request, response, error);
}
// 將保存下載封裝對象的屬性置nil
[strongSelf af_setImageDownloadReceipt:nil forState:state];
}
}];
// 用分類的屬性保存圖片下載封裝對象
[self af_setImageDownloadReceipt:receipt forState:state];
}
}
- (void)cancelImageDownloadTaskForState:(UIControlState)state {
// 經過狀態獲取到圖片下載封裝對象
AFImageDownloadReceipt *receipt = [self af_imageDownloadReceiptForState:state];
if (receipt != nil) {
// 根據獲取到的圖片下載封裝對象取消對應的圖片下載任務
[[self.class sharedImageDownloader] cancelTaskForImageDownloadReceipt:receipt];
// 把保存圖片下載封裝對象的屬性置nil
[self af_setImageDownloadReceipt:nil forState:state];
}
}
複製代碼
backgroundImage的實現和image的實現除了在設置圖片時一個給backgroundImage,一個給image以外都相同。網絡
- (void)setBackgroundImageForState:(UIControlState)state
withURL:(NSURL *)url
{
[self setBackgroundImageForState:state withURL:url placeholderImage:nil];
}
- (void)setBackgroundImageForState:(UIControlState)state
withURL:(NSURL *)url
placeholderImage:(nullable UIImage *)placeholderImage
{
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
[request addValue:@"image/*" forHTTPHeaderField:@"Accept"];
[self setBackgroundImageForState:state withURLRequest:request placeholderImage:placeholderImage success:nil failure:nil];
}
- (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
{
if ([self isActiveBackgroundTaskURLEqualToURLRequest:urlRequest forState:state]) {
return;
}
[self cancelBackgroundImageDownloadTaskForState:state];
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 setBackgroundImage:cachedImage forState:state];
}
[self af_setBackgroundImageDownloadReceipt:nil forState:state];
} else {
if (placeholderImage) {
[self setBackgroundImage:placeholderImage forState:state];
}
__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_backgroundImageDownloadReceiptForState:state].receiptID isEqual:downloadID]) {
if (success) {
success(request, response, responseObject);
} else if(responseObject) {
[strongSelf setBackgroundImage:responseObject forState:state];
}
[strongSelf af_setBackgroundImageDownloadReceipt:nil forState:state];
}
}
failure:^(NSURLRequest * _Nonnull request, NSHTTPURLResponse * _Nullable response, NSError * _Nonnull error) {
__strong __typeof(weakSelf)strongSelf = weakSelf;
if ([[strongSelf af_backgroundImageDownloadReceiptForState:state].receiptID isEqual:downloadID]) {
if (failure) {
failure(request, response, error);
}
[strongSelf af_setBackgroundImageDownloadReceipt:nil forState:state];
}
}];
[self af_setBackgroundImageDownloadReceipt:receipt forState:state];
}
}
- (void)cancelBackgroundImageDownloadTaskForState:(UIControlState)state {
AFImageDownloadReceipt *receipt = [self af_backgroundImageDownloadReceiptForState:state];
if (receipt != nil) {
[[self.class sharedImageDownloader] cancelTaskForImageDownloadReceipt:receipt];
[self af_setBackgroundImageDownloadReceipt:nil forState:state];
}
}
複製代碼
/**
判斷圖片下載請求是否已經正在進行中
*/
- (BOOL)isActiveTaskURLEqualToURLRequest:(NSURLRequest *)urlRequest forState:(UIControlState)state {
// 先根據狀態獲取到屬性中保存的圖片下載封裝對象
AFImageDownloadReceipt *receipt = [self af_imageDownloadReceiptForState:state];
// 根據二者的連接是否相同進行比較
return [receipt.task.originalRequest.URL.absoluteString isEqualToString:urlRequest.URL.absoluteString];
}
/**
判斷背景圖片下載請求是否已經正在進行中
*/
- (BOOL)isActiveBackgroundTaskURLEqualToURLRequest:(NSURLRequest *)urlRequest forState:(UIControlState)state {
// 實現和上面的方法相同
AFImageDownloadReceipt *receipt = [self af_backgroundImageDownloadReceiptForState:state];
return [receipt.task.originalRequest.URL.absoluteString isEqualToString:urlRequest.URL.absoluteString];
}
複製代碼
當咱們利用這個分類爲按鈕設置圖片時異步
源碼閱讀系列:AFNetworkingpost
源碼閱讀:AFNetworking(二)——AFURLRequestSerializationurl
源碼閱讀:AFNetworking(三)——AFURLResponseSerializationspa
源碼閱讀:AFNetworking(四)——AFSecurityPolicy3d
源碼閱讀:AFNetworking(五)——AFNetworkReachabilityManagercode
源碼閱讀:AFNetworking(六)——AFURLSessionManager
源碼閱讀:AFNetworking(七)——AFHTTPSessionManager
源碼閱讀:AFNetworking(八)——AFAutoPurgingImageCache
源碼閱讀:AFNetworking(九)——AFImageDownloader
源碼閱讀:AFNetworking(十)——AFNetworkActivityIndicatorManager
源碼閱讀:AFNetworking(十一)——UIActivityIndicatorView+AFNetworking
源碼閱讀:AFNetworking(十二)——UIButton+AFNetworking
源碼閱讀:AFNetworking(十三)——UIImageView+AFNetworking
源碼閱讀:AFNetworking(十四)——UIProgressView+AFNetworking