此系列文章都是轉(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