AFNetworking的源碼解讀馬上就結(jié)束了,這一篇應(yīng)該算是倒數(shù)第二篇辐啄,下一篇會(huì)是對(duì)AFNetworking中的技術(shù)點(diǎn)進(jìn)行總結(jié)采章。
前言
上一篇我們總結(jié)了 UIActivityIndicatorView UIRefreshControl UIImageView 這3個(gè)控件的分類。那么這一篇就總結(jié)下剩余的3個(gè)分類:UIButton UIProgressView UIWebView 壶辜。
UIButton+AFNetworking
UIButton跟圖片相關(guān)的屬性大概有兩個(gè)悯舟,Image
和BackgroundImage
.所以這個(gè)分類就是賦予他們異步加載圖片的能力。
其中核心方法為:
示例代碼:
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;
}
}
- (AFImageDownloadReceipt *)af_imageDownloadReceiptForState:(UIControlState)state {
return (AFImageDownloadReceipt *)objc_getAssociatedObject(self, af_imageDownloadReceiptKeyForState(state));
}
- (void)af_setImageDownloadReceipt:(AFImageDownloadReceipt *)imageDownloadReceipt
forState:(UIControlState)state
{
objc_setAssociatedObject(self, af_imageDownloadReceiptKeyForState(state), imageDownloadReceipt, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
我們分析下上邊的代碼砸民。我們都知道UIButton有4種狀態(tài)图谷。這個(gè)分類能夠支持不同的狀態(tài)加載不同的圖片。同樣我們也知道阱洪,每一個(gè)圖片的加載便贵,都需要一個(gè)AFImageDownloadReceipt憑證。所以冗荸,我們要為UIButton擴(kuò)展一個(gè)根據(jù)狀態(tài)獲取憑證的方法承璃,就是:af_imageDownloadReceiptForState:
。既然有獲取憑證的方法蚌本,就應(yīng)該有根據(jù)狀態(tài)設(shè)置憑證的方法盔粹,那就是:af_setImageDownloadReceipt: forState:
.
上邊的af_imageDownloadReceiptKeyForState
方法的作用就是為運(yùn)行時(shí)提供一個(gè)key,這個(gè)key是一個(gè)內(nèi)存地址程癌。也可使用@Selector()舷嗡。同理,下邊的代碼擴(kuò)展了BackgroundImage嵌莉,原理同上进萄,就不做解釋了
示例代碼:
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;
}
}
- (AFImageDownloadReceipt *)af_backgroundImageDownloadReceiptForState:(UIControlState)state {
return (AFImageDownloadReceipt *)objc_getAssociatedObject(self, af_backgroundImageDownloadReceiptKeyForState(state));
}
- (void)af_setBackgroundImageDownloadReceipt:(AFImageDownloadReceipt *)imageDownloadReceipt
forState:(UIControlState)state
{
objc_setAssociatedObject(self, af_backgroundImageDownloadReceiptKeyForState(state), imageDownloadReceipt, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
示例代碼:
// 使用運(yùn)行時(shí)設(shè)置sharedImageDownloader
+ (AFImageDownloader *)sharedImageDownloader {
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wgnu"
return objc_getAssociatedObject(self, @selector(sharedImageDownloader)) ?: [AFImageDownloader defaultInstance];
#pragma clang diagnostic pop
}
// 使用運(yùn)行時(shí)設(shè)置setSharedImageDownloader:
+ (void)setSharedImageDownloader:(AFImageDownloader *)imageDownloader {
objc_setAssociatedObject(self, @selector(sharedImageDownloader), imageDownloader, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
如果對(duì)這個(gè)分類的核心方法感興趣的話,可以參考上一篇锐峭。里邊有詳細(xì)的解釋中鼠,原理和代碼非常非常像,在這里為了節(jié)省篇幅就不做多余的說(shuō)明了沿癞。在這個(gè)分類中下邊圖片的那行代碼可以注釋掉援雇。
UIProgressView+AFNetworking
UIProgressView的這個(gè)分類,實(shí)現(xiàn)原理就是監(jiān)聽(tīng)NSURLSessionUploadTask
或者NSURLSessionDownloadTask
中的"state" "countOfBytesSent" "countOfBytesReceived" 椎扬。然后設(shè)置進(jìn)度就可以了惫搏。
示例代碼:
- (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);
}
<font color=orange>看到上邊的這四個(gè)方法,我突然間明白蚕涤,假如我們需要一個(gè)屬性記錄某一個(gè)狀態(tài)的話筐赔,通常我們會(huì)寫一個(gè)屬性,但是看上邊的代碼钻趋,是通過(guò)擴(kuò)展了幾個(gè)方法來(lái)達(dá)到記錄狀態(tài)的目的川陆。這就說(shuō)明同樣一個(gè)結(jié)果,可以有不同的實(shí)現(xiàn)手段。但我不太明白這兩個(gè)的區(qū)別是什么较沪?</font>
示例代碼:
- (void)setProgressWithUploadProgressOfTask:(NSURLSessionUploadTask *)task
animated:(BOOL)animated
{
[task addObserver:self forKeyPath:@"state" options:(NSKeyValueObservingOptions)0 context:AFTaskCountOfBytesSentContext];
[task addObserver:self forKeyPath:@"countOfBytesSent" options:(NSKeyValueObservingOptions)0 context:AFTaskCountOfBytesSentContext];
[self af_setUploadProgressAnimated:animated];
}
- (void)setProgressWithDownloadProgressOfTask:(NSURLSessionDownloadTask *)task
animated:(BOOL)animated
{
[task addObserver:self forKeyPath:@"state" options:(NSKeyValueObservingOptions)0 context:AFTaskCountOfBytesReceivedContext];
[task addObserver:self forKeyPath:@"countOfBytesReceived" options:(NSKeyValueObservingOptions)0 context:AFTaskCountOfBytesReceivedContext];
[self af_setDownloadProgressAnimated:animated];
}
示例代碼:
- (void)observeValueForKeyPath:(NSString *)keyPath
ofObject:(id)object
change:(__unused NSDictionary *)change
context:(void *)context
{
// 判斷是不是我們需要的監(jiān)聽(tīng)對(duì)象
if (context == AFTaskCountOfBytesSentContext || context == AFTaskCountOfBytesReceivedContext) {
// 上傳
if ([keyPath isEqualToString:NSStringFromSelector(@selector(countOfBytesSent))]) {
if ([object countOfBytesExpectedToSend] > 0) {
dispatch_async(dispatch_get_main_queue(), ^{
[self setProgress:[object countOfBytesSent] / ([object countOfBytesExpectedToSend] * 1.0f) animated:self.af_uploadProgressAnimated];
});
}
}
// 下載
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];
});
}
}
// 狀態(tài)
if ([keyPath isEqualToString:NSStringFromSelector(@selector(state))]) {
if ([(NSURLSessionTask *)object state] == NSURLSessionTaskStateCompleted) {
@try {
// 移除state
[object removeObserver:self forKeyPath:NSStringFromSelector(@selector(state))];
// 移除countOfBytesSent
if (context == AFTaskCountOfBytesSentContext) {
[object removeObserver:self forKeyPath:NSStringFromSelector(@selector(countOfBytesSent))];
}
// 移除countOfBytesReceived
if (context == AFTaskCountOfBytesReceivedContext) {
[object removeObserver:self forKeyPath:NSStringFromSelector(@selector(countOfBytesReceived))];
}
}
@catch (NSException * __unused exception) {}
}
}
}
}
UIWebView+AFNetworking
UIWebView的這個(gè)分類是這幾個(gè)分類中最讓我驚訝的一個(gè)鳞绕。讓我真正認(rèn)識(shí)到條條大路通羅馬到底是什么意思。有時(shí)候人的思想確實(shí)會(huì)被固有的思維所束縛尸曼。**這里只是用了UIWebView的loadData:(NSData *)data MIMEType:(NSString *)MIMEType textEncodingName:(NSString )textEncodingName baseURL:(NSURL )baseURL方法
你會(huì)發(fā)現(xiàn)使用這個(gè)分類配合UIWebView们何,所有的事情都變得很簡(jiǎn)單。
示例代碼:
@interface UIWebView (_AFNetworking)
@property (readwrite, nonatomic, strong, setter = af_setURLSessionTask:) NSURLSessionDataTask *af_URLSessionTask;
@end
@implementation UIWebView (_AFNetworking)
- (NSURLSessionDataTask *)af_URLSessionTask {
return (NSURLSessionDataTask *)objc_getAssociatedObject(self, @selector(af_URLSessionTask));
}
- (void)af_setURLSessionTask:(NSURLSessionDataTask *)af_URLSessionTask {
objc_setAssociatedObject(self, @selector(af_URLSessionTask), af_URLSessionTask, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
@end
為UIWebView擴(kuò)展了一個(gè)私有屬性af_URLSessionTask控轿,定義為每一次請(qǐng)求冤竹,就會(huì)對(duì)應(yīng)一個(gè)af_URLSessionTask。
示例代碼:
- (AFHTTPSessionManager *)sessionManager {
static AFHTTPSessionManager *_af_defaultHTTPSessionManager = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
_af_defaultHTTPSessionManager = [[AFHTTPSessionManager alloc] initWithSessionConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration]];
_af_defaultHTTPSessionManager.requestSerializer = [AFHTTPRequestSerializer serializer];
_af_defaultHTTPSessionManager.responseSerializer = [AFHTTPResponseSerializer serializer];
});
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wgnu"
return objc_getAssociatedObject(self, @selector(sessionManager)) ?: _af_defaultHTTPSessionManager;
#pragma clang diagnostic pop
}
- (void)setSessionManager:(AFHTTPSessionManager *)sessionManager {
objc_setAssociatedObject(self, @selector(sessionManager), sessionManager, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
為UIWebView擴(kuò)展的一個(gè)sessionManager屬性茬射。實(shí)現(xiàn)了setter和getter方法鹦蠕。這樣在后邊直接使用self.sessionManager
就可以,不用創(chuàng)建了在抛。
示例代碼:
- (void)loadRequest:(NSURLRequest *)request
MIMEType:(NSString *)MIMEType
textEncodingName:(NSString *)textEncodingName
progress:(NSProgress * _Nullable __autoreleasing * _Nullable)progress
success:(NSData * (^)(NSHTTPURLResponse *response, NSData *data))success
failure:(void (^)(NSError *error))failure
{
// 檢查參數(shù)
NSParameterAssert(request);
// 如果正處于運(yùn)行或者暫停裝狀態(tài)钟病,就取消之前的任務(wù)task并設(shè)置為nil
if (self.af_URLSessionTask.state == NSURLSessionTaskStateRunning || self.af_URLSessionTask.state == NSURLSessionTaskStateSuspended) {
[self.af_URLSessionTask cancel];
}
self.af_URLSessionTask = nil;
__weak __typeof(self)weakSelf = self;
NSURLSessionDataTask *dataTask;
dataTask = [self.sessionManager
GET:request.URL.absoluteString
parameters:nil
progress:nil
success:^(NSURLSessionDataTask * _Nonnull task, id _Nonnull responseObject) {
__strong __typeof(weakSelf) strongSelf = weakSelf;
// 請(qǐng)求成功后,調(diào)用success block
if (success) {
success((NSHTTPURLResponse *)task.response, responseObject);
}
// 顯示數(shù)據(jù)
[strongSelf loadData:responseObject MIMEType:MIMEType textEncodingName:textEncodingName baseURL:[task.currentRequest URL]];
// 調(diào)用webViewDidFinishLoad
if ([strongSelf.delegate respondsToSelector:@selector(webViewDidStartLoad:)]) {
[strongSelf.delegate webViewDidFinishLoad:strongSelf];
}
}
failure:^(NSURLSessionDataTask * _Nonnull task, NSError * _Nonnull error) {
if (failure) {
failure(error);
}
}];
self.af_URLSessionTask = dataTask;
// 設(shè)置progress刚梭,這個(gè)來(lái)自于self.sessionManager
if (progress != nil) {
*progress = [self.sessionManager downloadProgressForTask:dataTask];
}
// 開(kāi)啟任務(wù)
[self.af_URLSessionTask resume];
// 調(diào)用webViewDidStartLoad方法
if ([self.delegate respondsToSelector:@selector(webViewDidStartLoad:)]) {
[self.delegate webViewDidStartLoad:self];
}
}
--
- (void)loadRequest:(NSURLRequest *)request
progress:(NSProgress * _Nullable __autoreleasing * _Nullable)progress
success:(NSString * (^)(NSHTTPURLResponse *response, NSString *HTML))success
failure:(void (^)(NSError *error))failure
{
[self loadRequest:request MIMEType:nil textEncodingName:nil progress:progress success:^NSData *(NSHTTPURLResponse *response, NSData *data) {
NSStringEncoding stringEncoding = NSUTF8StringEncoding;
if (response.textEncodingName) {
CFStringEncoding encoding = CFStringConvertIANACharSetNameToEncoding((CFStringRef)response.textEncodingName);
if (encoding != kCFStringEncodingInvalidId) {
stringEncoding = CFStringConvertEncodingToNSStringEncoding(encoding);
}
}
NSString *string = [[NSString alloc] initWithData:data encoding:stringEncoding];
if (success) {
string = success(response, string);
}
return [string dataUsingEncoding:stringEncoding];
} failure:failure];
}
總結(jié)
就一句話肠阱,UIWebView+AFNetworking模擬了UIWebView加載數(shù)據(jù)的過(guò)程。模擬朴读,模擬屹徘,模擬。衅金。噪伊。
推薦閱讀
AFNetworking 3.0 源碼解讀(一)之 AFNetworkReachabilityManager
AFNetworking 3.0 源碼解讀(二)之 AFSecurityPolicy
AFNetworking 3.0 源碼解讀(三)之 AFURLRequestSerialization
AFNetworking 3.0 源碼解讀(四)之 AFURLResponseSerialization
AFNetworking 3.0 源碼解讀(五)之 AFURLSessionManager
AFNetworking 3.0 源碼解讀(六)之 AFHTTPSessionManager
AFNetworking 3.0 源碼解讀(七)之 AFAutoPurgingImageCache
AFNetworking 3.0 源碼解讀(八)之 AFImageDownloader
AFNetworking 3.0 源碼解讀(九)之 AFNetworkActivityIndicatorManager
AFNetworking 3.0 源碼解讀(十)之 UIActivityIndicatorView/UIRefreshControl/UIImageView + AFNetworking