AFNetworking 3.0 源碼解讀(一)之 AFNetworkReachabilityManager

此系列文章都是轉(zhuǎn)載文章 ? ?文章出處


做ios開(kāi)發(fā),AFNetworking 這個(gè)網(wǎng)絡(luò)框架肯定都非常熟悉,也許我們平時(shí)只使用了它的部分功能,而且我們對(duì)它的實(shí)現(xiàn)原理并不是很清楚,就好像總是有一團(tuán)迷霧在眼前一樣。

接下來(lái)我們就非常詳細(xì)的來(lái)讀一讀這個(gè)框架的代碼,我們的目標(biāo)就是理解了它的思想之后,能夠明白我們的請(qǐng)求是如何實(shí)現(xiàn)的,我們的代碼哪里還需要進(jìn)行改進(jìn),如果能夠更進(jìn)一步,我們能夠總結(jié)出一套適合大部分應(yīng)用的網(wǎng)絡(luò)架構(gòu)思想。

能夠讓一些人從中受益。

我們先來(lái)看看整個(gè)框架的文件系統(tǒng),我們先不對(duì)每個(gè)文件的作用進(jìn)行說(shuō)明,在整個(gè)源碼解讀最后的一篇中我們會(huì)對(duì)整個(gè)框架進(jìn)行總結(jié)。會(huì)有一張清晰的圖表來(lái)說(shuō)明這個(gè)問(wèn)題。

我們?cè)诳匆粋€(gè)框架的時(shí)候呢,可以這樣先看,先看每個(gè)文件的頭文件,也就是.h文件

可以看到,有的頭文件是包含了別的頭文件的,在不考慮系統(tǒng)的頭文件的情況下,我們能夠發(fā)現(xiàn)一些比較獨(dú)立的類,從上圖中,我們可以看出

比較獨(dú)立的類有:

1.AFURLResponseSerialization.h

2.AFNetworkReachabilityManager.h

3.AFURLRequestSerialization.h

4.AFSecurityPolicy.h

本篇就介紹AFNetworkReachabilityManager.h的內(nèi)容,這個(gè)是用來(lái)監(jiān)控網(wǎng)絡(luò)環(huán)境變化的類。

#import

通過(guò)導(dǎo)入了這個(gè)頭文件,我們得知:網(wǎng)絡(luò)監(jiān)控的實(shí)現(xiàn)是依賴SystemConfiguration這個(gè)api的。說(shuō)明這個(gè)api能夠提供這樣的功能,至少讓我們明白了我們平時(shí)都會(huì)導(dǎo)入它的一個(gè)用途。

這是一個(gè)枚舉封裝,還是遵循一個(gè)使用枚舉的原則,當(dāng)滿足一個(gè)有限的并具有統(tǒng)一主題的集合的時(shí)候,我們就考慮枚舉。在這里作者是枚舉了4種類型。這幾種類型能夠滿足我們開(kāi)發(fā)中大部分的功能,如果不滿足,可以自行進(jìn)行擴(kuò)展。

NS_ASSUME_NONNULL_BEGIN

NS_ASSUME_NONNULL_END

這個(gè)是為了swift的可選類型配添加的,在這兩個(gè)終點(diǎn)的內(nèi)容的參數(shù)默認(rèn)都是nonnull的。

這段文字是對(duì)這個(gè)類的說(shuō)明。我們估且不去管它說(shuō)了什么,在看看蘋果官方的

*** 這樣的內(nèi)容會(huì)出現(xiàn)在一個(gè)屬性或者方法的上方,目的是對(duì)其內(nèi)容的解釋。我看到這里就想到了我們平時(shí)的開(kāi)發(fā),我們能夠把每段代碼都當(dāng)成是api的開(kāi)發(fā),也把注釋寫的詳細(xì)一點(diǎn)。曾經(jīng)看過(guò)兩種不同的說(shuō)辭,一種是說(shuō)把代碼注釋盡量少些,要求代碼簡(jiǎn)介可讀性強(qiáng)。另一種是說(shuō)注釋要詳細(xì),著重考慮他人讀代碼的感受。個(gè)人感覺(jué)還是寫詳細(xì)一點(diǎn)比較好,因?yàn)榭赡苓^(guò)一段時(shí)間之后,自己再去看自己當(dāng)時(shí)寫的代碼可能就不記得了。很有可能在寫這些繁瑣的注釋的過(guò)程中,能夠想到些什么,比如如何合并掉一些沒(méi)必要的方法等等。

本類提供了四個(gè)只讀的屬性來(lái)讓我們獲取我們需要的內(nèi)容

1. 網(wǎng)絡(luò)狀態(tài)

2. 是否是可達(dá)的

3. 當(dāng)前連接是否是WWAN

4. 當(dāng)前連接是夠是WiFi

四個(gè)屬性均為只讀屬性,只給了用戶訪問(wèn)權(quán),注意BOOL屬性一般是要寫getter方法的。

作者使用了這個(gè)來(lái)分隔同一類中不同功能模塊。這個(gè)算是個(gè)人習(xí)慣問(wèn)題吧。舉個(gè)平時(shí)開(kāi)發(fā)的例子,在.m文件中我個(gè)人使用#pragma mark 分隔不同功能。

提供了5中初始化方法,能夠滿足大部分的需求。

SCNetworkReachabilityRef 這個(gè)很重要,這個(gè)類的就是基于它開(kāi)發(fā)的。

+ (instancetype)managerForDomain:(NSString*)domain; 監(jiān)聽(tīng)制定domain的網(wǎng)絡(luò)狀態(tài)。

+ (instancetype)managerForAddress:(constvoid*)address; 監(jiān)聽(tīng)某個(gè)socket地址的網(wǎng)絡(luò)狀態(tài),socket通信請(qǐng)看這篇文章:socket通信

打開(kāi)和關(guān)閉監(jiān)聽(tīng)的方法。

返回一個(gè)網(wǎng)絡(luò)狀態(tài)的本地語(yǔ)言的字符串。往往我們可以根據(jù)這個(gè)字符串來(lái)告訴用戶,當(dāng)前網(wǎng)絡(luò)發(fā)生了什么,當(dāng)然,也可以根據(jù)狀態(tài)自定義提示文字。

設(shè)置網(wǎng)絡(luò)轉(zhuǎn)態(tài)改變的回調(diào),監(jiān)聽(tīng)網(wǎng)絡(luò)改變的回調(diào)有兩種方式:

1.使用上邊的這個(gè)方法。

2.監(jiān)聽(tīng)AFNetworkingReachabilityDidChangeNotification通知。

這個(gè)是與網(wǎng)絡(luò)狀態(tài)變化相關(guān)的通知。接受的通知中會(huì)有一個(gè)userinfo 是一個(gè)NSDictionary 其中key就是

AFNetworkingReachabilityNotificationStatusItem

*** 這簡(jiǎn)單的兩行代碼能夠告訴我們的是,我們平時(shí)的開(kāi)發(fā)中 但凡設(shè)計(jì)到發(fā)通知的功能,我們應(yīng)該把通知的字符串封裝到一個(gè)專有的文件中,同時(shí)在文件內(nèi)部按不同模塊進(jìn)行區(qū)分,當(dāng)然必要的注釋也很有必要。

ps:FOUNDATION_EXPORT 和#define 都能定義常量。FOUNDATION_EXPORT 能夠使用==進(jìn)行判斷,效率略高。而且能夠隱藏定義細(xì)節(jié)(就是實(shí)現(xiàn)部分不在.中)

對(duì)函數(shù):根據(jù)狀態(tài)獲取字符串 ?聲明。

好了,這個(gè)類的.h文件我們已經(jīng)非常相信的進(jìn)行解讀了,我們并不是大概的說(shuō)了下他提供的功能,而是通過(guò)讀每行代碼,我們能聯(lián)想到什么,什么東西能幫助我們更好的編程。

我們接著看AFNetworkReachabilityManager.m 的內(nèi)容

這幾個(gè)頭文件是系統(tǒng)庫(kù),是為了后邊的sockaddr_in6 /sockaddr_in 準(zhǔn)備的,不熟悉的可以看這篇文章socket通信

這幾個(gè)就沒(méi)什么好說(shuō)的了,我們接著看

這個(gè)方法是對(duì).h 中最后一個(gè)方法的實(shí)現(xiàn)。指的我們注意的是NSLocalizedStringFromTable這個(gè)宏。為什么要注意它呢?

這就涉及到本地國(guó)際化的問(wèn)題。所謂的國(guó)際化就是讓你的app能夠根據(jù)不同的語(yǔ)言顯示相對(duì)應(yīng)的語(yǔ)言。

*** 但這并不簡(jiǎn)單,沒(méi)有經(jīng)驗(yàn)的開(kāi)發(fā)人員,一開(kāi)始可能不會(huì)做這樣的設(shè)置,如果日后需要國(guó)際話了,在做就很麻煩了。所以說(shuō)在開(kāi)中,但凡使用到字符串的地方都要考慮語(yǔ)言的不同。不同的語(yǔ)言下,一個(gè)意思的表達(dá)所使用的字符串長(zhǎng)度是不一樣的,這就影射出空間的寬度可能會(huì)不一樣。

好了,國(guó)際化的內(nèi)容就不說(shuō)了,請(qǐng)自行搜索。

1/**2*? 根據(jù)SCNetworkReachabilityFlags這個(gè)網(wǎng)絡(luò)標(biāo)記來(lái)轉(zhuǎn)換成我們?cè)陂_(kāi)發(fā)中經(jīng)常使用的網(wǎng)絡(luò)狀態(tài)31.不能連接網(wǎng)絡(luò)42.蜂窩連接53.WiFi連接64.未知連接7*/8staticAFNetworkReachabilityStatus AFNetworkReachabilityStatusForFlags(SCNetworkReachabilityFlags flags) {910//是否能夠到達(dá)11BOOL isReachable = ((flags & kSCNetworkReachabilityFlagsReachable) !=0);1213//在聯(lián)網(wǎng)之前需要建立連接14BOOL needsConnection = ((flags & kSCNetworkReachabilityFlagsConnectionRequired) !=0);1516//是否可以自動(dòng)連接17BOOL canConnectionAutomatically = (((flags & kSCNetworkReachabilityFlagsConnectionOnDemand ) !=0) || ((flags & kSCNetworkReachabilityFlagsConnectionOnTraffic) !=0));1819//是否可以連接,在不需要用戶手動(dòng)設(shè)置的前提下20BOOL canConnectWithoutUserInteraction = (canConnectionAutomatically && (flags & kSCNetworkReachabilityFlagsInterventionRequired) ==0);2122//是否可以聯(lián)網(wǎng)的條件 1.能夠到達(dá) 2.不需要建立連接或者不需要用戶手動(dòng)設(shè)置連接 就表示能夠連接到網(wǎng)絡(luò)23BOOL isNetworkReachable = (isReachable && (!needsConnection ||canConnectWithoutUserInteraction));2425AFNetworkReachabilityStatus status =AFNetworkReachabilityStatusUnknown;26if(isNetworkReachable ==NO) {27status =AFNetworkReachabilityStatusNotReachable;28}29#ifTARGET_OS_IPHONE30elseif((flags & kSCNetworkReachabilityFlagsIsWWAN) !=0) {31status =AFNetworkReachabilityStatusReachableViaWWAN;32}33#endif34else{35status =AFNetworkReachabilityStatusReachableViaWiFi;36}3738returnstatus;39}

這個(gè)方法根據(jù)SCNetworkReachabilityFlags這個(gè)標(biāo)記轉(zhuǎn)換成我們自定義的枚舉類型。至于轉(zhuǎn)換規(guī)則,上邊的代碼中注釋部分寫的很清楚。

*** 在這里不得不多說(shuō)幾句,很多框架中都會(huì)把一個(gè)類中的私有方法寫成這樣。為什么呢? 我們?cè)陂_(kāi)發(fā)中經(jīng)常會(huì)寫成- (void)funcName; 這樣的私有方法。

我個(gè)人的意見(jiàn)是一個(gè)類中的私有方法寫成static void funcName() 這樣的c函數(shù)比較好。

1. 在文件的最前方,比較容易查找

2. 可以適當(dāng)?shù)氖褂脙?nèi)聯(lián)函數(shù),提高效率。

根據(jù)一個(gè)標(biāo)識(shí) 來(lái)處理Block和通知。保證兩者同一狀態(tài)。

need-to-insert-img

包含了 類中需要處理的屬性。

need-to-insert-img

來(lái)看這個(gè)最基本的初始化方法,初始化了自身的屬性。

CFRetain()后要記得CFRelease().

need-to-insert-img

通過(guò)一個(gè)socket地址來(lái)初始化。 首先新建SCNetworkReachabilityRef 對(duì)象,然后調(diào)用initWithReachability: 方法。記得手動(dòng)管理內(nèi)存。

need-to-insert-img

這個(gè)方法基本同上。

綜合上邊兩個(gè)方法,我們發(fā)現(xiàn)?SCNetworkReachabilityRef 有兩個(gè)創(chuàng)建方法:

1.SCNetworkReachabilityCreateWithName

2.SCNetworkReachabilityCreateWithAddress

need-to-insert-img

由于IPv6 是ios9和os_x 10.11后邊推出的,所有要進(jìn)行版本判斷。這禮拜呢設(shè)計(jì)到的socket的知識(shí),請(qǐng)看socket通信

通過(guò)這段代碼我們能學(xué)到什么呢?

1,方法的創(chuàng)建也是有順序的,可以使用函數(shù)訪問(wèn)函數(shù)的思想。

2. @if 這樣的預(yù)編譯指令能夠替換掉代碼中部分if else 。好處就是代碼會(huì)不會(huì)被編譯的區(qū)別。

need-to-insert-img

單例的寫法。

need-to-insert-img

對(duì)需要釋放時(shí),做一些處理。

need-to-insert-img

這個(gè)是.h文件暴露出來(lái)的3個(gè)BOOL 屬性的getter方法,注意,由于我們?cè)贎property中定義了getter方法,所以getter方法就要寫成我們定義的那種。

從這3個(gè)方法中也能看出,函數(shù)嵌套的思想還是很重要,要想做到這一點(diǎn),只能是多想才行。

need-to-insert-img

這個(gè)算是這個(gè)類的核心方法,設(shè)置監(jiān)聽(tīng)網(wǎng)咯監(jiān)聽(tīng)。

我們先來(lái)了解下基礎(chǔ)知識(shí)。

SCNetworkReachabilityContext

點(diǎn)進(jìn)去,會(huì)發(fā)現(xiàn)這是一個(gè)結(jié)構(gòu)體,一般c語(yǔ)言的結(jié)構(gòu)體是對(duì)要保存的數(shù)據(jù)的一種描述

need-to-insert-img

1. 第一個(gè)參數(shù)接受一個(gè)signedlong 的參數(shù)

2. 第二個(gè)參數(shù)接受一個(gè)void * 類型的值,相當(dāng)于oc的id類型,void * 可以指向任何類型的參數(shù)

3. 第三個(gè)參數(shù) 是一個(gè)函數(shù) 目的是對(duì)info做retain操作,

4. 第四個(gè)參數(shù)是一個(gè)函數(shù),目的是對(duì)info做release操作

5. 第五個(gè)參數(shù)是 一個(gè)函數(shù),根據(jù)info獲取Description字符串

在這里我們要攜帶的這個(gè)info就是下邊的這個(gè)block

need-to-insert-img

1__weak __typeof(self)weakSelf =self;2AFNetworkReachabilityStatusBlock callback = ^(AFNetworkReachabilityStatus status) {3__strong __typeof(weakSelf)strongSelf =weakSelf;45strongSelf.networkReachabilityStatus =status;6if(strongSelf.networkReachabilityStatusBlock) {7strongSelf.networkReachabilityStatusBlock(status);8}910};

need-to-insert-img

retain和release 函數(shù)是下邊的這兩個(gè)函數(shù)

need-to-insert-img

1staticconstvoid* AFNetworkReachabilityRetainCallback(constvoid*info) {2returnBlock_copy(info);3}45staticvoidAFNetworkReachabilityReleaseCallback(constvoid*info) {6if(info) {7Block_release(info);8}9}

need-to-insert-img

設(shè)置網(wǎng)絡(luò)監(jiān)控分為下邊幾個(gè)步驟:

1.我們先新建上下文

1SCNetworkReachabilityContext context = {0, (__bridgevoid*)callback, AFNetworkReachabilityRetainCallback, AFNetworkReachabilityReleaseCallback, NULL};

2.設(shè)置回調(diào)

1SCNetworkReachabilitySetCallback(self.networkReachability, AFNetworkReachabilityCallback, &context);

其中這個(gè)AFNetworkReachabilityCallback 是這樣被定義的一個(gè)函數(shù)

typedefvoid(*SCNetworkReachabilityCallBack)? ? (

SCNetworkReachabilityRef? ? ? ? ? ? target,

SCNetworkReachabilityFlags? ? ? ? ? ? flags,void*__nullable? ? info

);

在本類中

1staticvoidAFNetworkReachabilityCallback(SCNetworkReachabilityRef __unused target, SCNetworkReachabilityFlags flags,void*info) {2AFPostReachabilityStatusChange(flags, (__bridge AFNetworkReachabilityStatusBlock)info);3}

3.加入RunLoop池

1SCNetworkReachabilityScheduleWithRunLoop(self.networkReachability, CFRunLoopGetMain(), kCFRunLoopCommonModes);

其中CFRunLoopGetMain()代表主RunLoop

ok,差不多已經(jīng)完成

need-to-insert-img

在異步線程 發(fā)送一次當(dāng)前的網(wǎng)絡(luò)狀態(tài)。

need-to-insert-img

停止網(wǎng)絡(luò)監(jiān)控

need-to-insert-img

這兩個(gè)方法沒(méi)什么好說(shuō)的了,一個(gè)是getter 一個(gè)是setter

need-to-insert-img

注冊(cè)鍵值依賴,這個(gè)可能大家平時(shí)用的比較少??梢粤私庖幌?/p>

比如說(shuō)一個(gè)類User中有兩個(gè)屬性

need-to-insert-img

還有一個(gè)卡片的類card

need-to-insert-img

我們寫一個(gè)info的setter 和 getter ?方法,

need-to-insert-img

need-to-insert-img

1@interfaceUser :NSObject2@property (nonatomic,copy)NSString *name;3@property (nonatomic,assign)NSUInteger age;4@end5678@interfacecard :NSObject9@property (nonatomic,copy)NSString *info;10@property (nonatomic,strong)User *user;11@end12@implementationcard1314- (NSString *)info {15return[NSString stringWithFormat:@"%@/%lu",_user.name,(unsignedlong)_user.age];16}17- (void)setInfo:(NSString *)info {1819NSArray *array = [info componentsSeparatedByString:@"/"];20_user.name = array[0];21_user.age = [array[1] integerValue];2223}2425+ (NSSet *)keyPathsForValuesAffectingValueForKey:(NSString *)key {26NSSet * keyPaths =[super keyPathsForValuesAffectingValueForKey:key];27NSArray * moreKeyPaths =nil;2829if([key isEqualToString:@"info"])30{31moreKeyPaths = [NSArray arrayWithObjects:@"user.name",@"user.age", nil];32}3334if(moreKeyPaths)35{36keyPaths =[keyPaths setByAddingObjectsFromArray:moreKeyPaths];37}3839returnkeyPaths;40}4142@end

need-to-insert-img

代碼差不多就是上邊的。我們可以監(jiān)聽(tīng)card的info屬性,當(dāng)user中的name或者age的值發(fā)生改變的時(shí)候,就會(huì)觸發(fā)info的鍵值監(jiān)聽(tīng)方法。這就是鍵值依賴的作用。

好了 本篇文章就到此為止了。下一篇會(huì)是AFSecurityPolicy

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容