天天看點

AFNetworking源碼:AFNetworking中的那些巧妙設計

雲栖号資訊:【 點選檢視更多行業資訊

在這裡您可以找到不同行業的第一手的上雲資訊,還在等什麼,快來!

我們都知道AFNetworking是一個非常好用且常見的網絡庫,那麼AFNetworking的開發者是如何做到的呢?AFNetworking中有哪些巧妙設計是我們還不知道,以後開發中可以借鑒的呢?

一、利用runtime黑魔法

1.方法交換(swizzle)

目的:

   這裡方法替換的目的主要是想在調用系統的NSURLSessionTask 的resume方法時,能夠發送AFNSURLSessionTaskDidResumeNotification通知,以達到監測系統方法調用的目的。

實作:

   _AFURLSessionTaskSwizzling類在+load方法中将_AFURLSessionTaskSwizzling 中的af_resume方法與NSURLSessionTask的resume方法交換。

AFNetworking源碼:AFNetworking中的那些巧妙設計

@end

@implementation _AFURLSessionTaskSwizzling

  • (void)load { if (NSClassFromString(@"NSURLSessionTask")) { NSURLSessionConfiguration configuration = [NSURLSessionConfiguration ephemeralSessionConfiguration]; NSURLSession session = [NSURLSession sessionWithConfiguration:configuration];
AFNetworking源碼:AFNetworking中的那些巧妙設計
  • (void)af_resume { NSAssert([self respondsToSelector:@selector(state)], @"Does not respond to state"); NSURLSessionTaskState state = [self state]; [self af_resume];

if (state != NSURLSessionTaskStateRunning) { [[NSNotificationCenter defaultCenter] postNotificationName:AFNSURLSessionTaskDidResumeNotification object:self]; } }

疑問

   通常我們需要實作這種操作的方式是實作一個子類,然後使用的時候使用子類。但是AFNetworking并不想改變我們使用NSURLSessionTask的方式,是以采用了這種巧妙的方式。

到這裡大部分人可能會有以下三個疑問,了解了這幾個疑問也就了解了為什麼說這裡設計很巧妙。

  • a .為什麼要在+load中實作交換? 因為load方法的實作肯定是在方法調用之前,在這裡實作交換可以確定調用在交換之後發生。 load方法裡實作還有一個好處,那就這個方法由系統自動調用,不用去在乎調用時機和由誰發起調用。_AFURLSessionTaskSwizzling是一個内嵌類,也就是說這個類隻在.m中定義和實作不需要暴露給使用者,這個類的唯一作用就是替換方法,也不需要被執行個體化或者被别的執行個體引用。
  • b.af_resume的實作裡又調用了[self af_resume],不會造成死循環嗎? 解釋這個問題很簡單,因為知道方法交換的原理就不難了解了。 替換之前:NSURLSessionTask的resume.png

替換之後:resume的調用.png可以看到

1 . 給resume發送消息的時候,實際是調用af_resume的實作。    

2 .在af_resume中給af_resume 發送消息,實際是調用resume的實作。

  • c.為什麼想替換NSURLSessionTask的resume方法沒有直接使用NSURLSessionTask類,而是通過周遊localDataTask的父類逐級替換? 這個疑問其實在代碼注釋中已經給出了解釋:
AFNetworking源碼:AFNetworking中的那些巧妙設計
AFNetworking源碼:AFNetworking中的那些巧妙設計

大意是:

  1. 在OC的實作中,NSURLSessionTask的類并不是NSURLSessionTask而是依靠類族. 也就是[NSURLSessionTask class]傳回的結果并不是我們想要的結果,NSCFURLSessionTask才是實際的類。    
  2. iOS8中的resmue是唯一的實作,而iOS7中NSCFLocalSessionTask并沒有調用super的resume,NSCFURLSessionTask和NSCFLocalSessionTask都實作了resume,是以需要循環調用superclass把兩個實作都替換掉。 是以開發者采用了這種方式確定所有版本的所有resume方法都會被替換掉。

2.關聯變量

目的

在UIImageView的分類中的類方法中,給UIImageView的類添加關聯變量。

實作

AFNetworking源碼:AFNetworking中的那些巧妙設計
  • (AFImageDownloader *)sharedImageDownloader { return objc_getAssociatedObject([UIImageView class], @selector(sharedImageDownloader)) ?: [AFImageDownloader defaultInstance]; }
  • (void)setSharedImageDownloader:(AFImageDownloader *)imageDownloader { objc_setAssociatedObject([UIImageView class], @selector(sharedImageDownloader), imageDownloader, OBJC_ASSOCIATION_RETAIN_NONATOMIC); }

同樣的了解了一下幾個疑問的原因,也就知道了設計的巧妙之處。

a .為什麼要用分類和關聯變量? 不用改變UIImageView的類,也不用繼承。

b .為什麼要在UIImageView類中添加關聯變量? 因為這個imageDownloader是屬于所有UIImageView的,并不屬于某一個UIImageView的執行個體。也就是說所有UIImageView的執行個體都是使用這個imageDownloader去請求圖檔。是以把imageDownloader與UIImageView的類關聯是合理的。

c .這麼做的好處? 調用imageDownloader有類似單例的便捷:

(void)testResponseIsNilWhenLoadedFromCache { AFImageDownloader *downloader = [UIImageView sharedImageDownloader]; ... }

其實這裡設計的巧妙之處不僅是這些,關聯變量的key使用@selector(sharedImageDownloader)也是一個很巧妙的應用,因為這樣就不需要單獨去聲明一個key,而且利用了屬性本身的名稱,即簡單又明了。

【雲栖号線上課堂】每天都有産品技術專家分享!

課程位址:

https://yqh.aliyun.com/zhibo

立即加入社群,與專家面對面,及時了解課程最新動态!

【雲栖号線上課堂 社群】

https://c.tb.cn/F3.Z8gvnK

原文釋出時間:2020-04-09

本文作者:jlstmac

本文來自:“cocoachina”,了解相關資訊可以關注“

cocoachina

繼續閱讀