此系列文章都是轉(zhuǎn)載文章 ? ?文章出處
做ios開發(fā)藕各,AFNetworking 這個網(wǎng)絡(luò)框架肯定都非常熟悉萎河,也許我們平時只使用了它的部分功能臊岸,而且我們對它的實現(xiàn)原理并不是很清楚橙数,就好像總是有一團迷霧在眼前一樣。
接下來我們就非常詳細的來讀一讀這個框架的代碼帅戒,我們的目標(biāo)就是理解了它的思想之后商模,能夠明白我們的請求是如何實現(xiàn)的,我們的代碼哪里還需要進行改進,如果能夠更進一步施流,我們能夠總結(jié)出一套適合大部分應(yīng)用的網(wǎng)絡(luò)架構(gòu)思想响疚。
能夠讓一些人從中受益。
我們先來看看整個框架的文件系統(tǒng)瞪醋,我們先不對每個文件的作用進行說明忿晕,在整個源碼解讀最后的一篇中我們會對整個框架進行總結(jié)。會有一張清晰的圖表來說明這個問題银受。
我們在看一個框架的時候呢践盼,可以這樣先看,先看每個文件的頭文件宾巍,也就是.h文件
可以看到咕幻,有的頭文件是包含了別的頭文件的,在不考慮系統(tǒng)的頭文件的情況下顶霞,我們能夠發(fā)現(xiàn)一些比較獨立的類肄程,從上圖中,我們可以看出
比較獨立的類有:
1.AFURLResponseSerialization.h
2.AFNetworkReachabilityManager.h
3.AFURLRequestSerialization.h
4.AFSecurityPolicy.h
本篇就介紹AFNetworkReachabilityManager.h的內(nèi)容选浑,這個是用來監(jiān)控網(wǎng)絡(luò)環(huán)境變化的類蓝厌。
#import
通過導(dǎo)入了這個頭文件,我們得知:網(wǎng)絡(luò)監(jiān)控的實現(xiàn)是依賴SystemConfiguration這個api的古徒。說明這個api能夠提供這樣的功能拓提,至少讓我們明白了我們平時都會導(dǎo)入它的一個用途。
這是一個枚舉封裝隧膘,還是遵循一個使用枚舉的原則代态,當(dāng)滿足一個有限的并具有統(tǒng)一主題的集合的時候,我們就考慮枚舉疹吃。在這里作者是枚舉了4種類型胆数。這幾種類型能夠滿足我們開發(fā)中大部分的功能,如果不滿足互墓,可以自行進行擴展。
NS_ASSUME_NONNULL_BEGIN
NS_ASSUME_NONNULL_END
這個是為了swift的可選類型配添加的蒋搜,在這兩個終點的內(nèi)容的參數(shù)默認都是nonnull的篡撵。
這段文字是對這個類的說明。我們估且不去管它說了什么豆挽,在看看蘋果官方的
*** 這樣的內(nèi)容會出現(xiàn)在一個屬性或者方法的上方育谬,目的是對其內(nèi)容的解釋。我看到這里就想到了我們平時的開發(fā)帮哈,我們能夠把每段代碼都當(dāng)成是api的開發(fā)膛檀,也把注釋寫的詳細一點。曾經(jīng)看過兩種不同的說辭,一種是說把代碼注釋盡量少些咖刃,要求代碼簡介可讀性強泳炉。另一種是說注釋要詳細,著重考慮他人讀代碼的感受嚎杨。個人感覺還是寫詳細一點比較好花鹅,因為可能過一段時間之后,自己再去看自己當(dāng)時寫的代碼可能就不記得了枫浙。很有可能在寫這些繁瑣的注釋的過程中刨肃,能夠想到些什么,比如如何合并掉一些沒必要的方法等等箩帚。
本類提供了四個只讀的屬性來讓我們獲取我們需要的內(nèi)容
1. 網(wǎng)絡(luò)狀態(tài)
2. 是否是可達的
3. 當(dāng)前連接是否是WWAN
4. 當(dāng)前連接是夠是WiFi
四個屬性均為只讀屬性真友,只給了用戶訪問權(quán),注意BOOL屬性一般是要寫getter方法的紧帕。
作者使用了這個來分隔同一類中不同功能模塊盔然。這個算是個人習(xí)慣問題吧。舉個平時開發(fā)的例子焕参,在.m文件中我個人使用#pragma mark 分隔不同功能轻纪。
提供了5中初始化方法,能夠滿足大部分的需求叠纷。
SCNetworkReachabilityRef 這個很重要刻帚,這個類的就是基于它開發(fā)的。
+ (instancetype)managerForDomain:(NSString*)domain; 監(jiān)聽制定domain的網(wǎng)絡(luò)狀態(tài)涩嚣。
+ (instancetype)managerForAddress:(constvoid*)address; 監(jiān)聽某個socket地址的網(wǎng)絡(luò)狀態(tài)崇众,socket通信請看這篇文章:socket通信
打開和關(guān)閉監(jiān)聽的方法。
返回一個網(wǎng)絡(luò)狀態(tài)的本地語言的字符串航厚。往往我們可以根據(jù)這個字符串來告訴用戶顷歌,當(dāng)前網(wǎng)絡(luò)發(fā)生了什么,當(dāng)然幔睬,也可以根據(jù)狀態(tài)自定義提示文字眯漩。
設(shè)置網(wǎng)絡(luò)轉(zhuǎn)態(tài)改變的回調(diào),監(jiān)聽網(wǎng)絡(luò)改變的回調(diào)有兩種方式:
1.使用上邊的這個方法麻顶。
2.監(jiān)聽AFNetworkingReachabilityDidChangeNotification通知赦抖。
這個是與網(wǎng)絡(luò)狀態(tài)變化相關(guān)的通知。接受的通知中會有一個userinfo 是一個NSDictionary 其中key就是
AFNetworkingReachabilityNotificationStatusItem
*** 這簡單的兩行代碼能夠告訴我們的是辅肾,我們平時的開發(fā)中 但凡設(shè)計到發(fā)通知的功能队萤,我們應(yīng)該把通知的字符串封裝到一個專有的文件中,同時在文件內(nèi)部按不同模塊進行區(qū)分矫钓,當(dāng)然必要的注釋也很有必要要尔。
ps:FOUNDATION_EXPORT 和#define 都能定義常量舍杜。FOUNDATION_EXPORT 能夠使用==進行判斷,效率略高赵辕。而且能夠隱藏定義細節(jié)(就是實現(xiàn)部分不在.中)
對函數(shù):根據(jù)狀態(tài)獲取字符串 ?聲明既绩。
好了,這個類的.h文件我們已經(jīng)非常相信的進行解讀了匆帚,我們并不是大概的說了下他提供的功能熬词,而是通過讀每行代碼,我們能聯(lián)想到什么吸重,什么東西能幫助我們更好的編程互拾。
我們接著看AFNetworkReachabilityManager.m 的內(nèi)容
這幾個頭文件是系統(tǒng)庫,是為了后邊的sockaddr_in6 /sockaddr_in 準(zhǔn)備的嚎幸,不熟悉的可以看這篇文章socket通信
這幾個就沒什么好說的了颜矿,我們接著看
這個方法是對.h 中最后一個方法的實現(xiàn)。指的我們注意的是NSLocalizedStringFromTable這個宏嫉晶。為什么要注意它呢骑疆?
這就涉及到本地國際化的問題。所謂的國際化就是讓你的app能夠根據(jù)不同的語言顯示相對應(yīng)的語言替废。
*** 但這并不簡單箍铭,沒有經(jīng)驗的開發(fā)人員,一開始可能不會做這樣的設(shè)置椎镣,如果日后需要國際話了诈火,在做就很麻煩了。所以說在開中状答,但凡使用到字符串的地方都要考慮語言的不同冷守。不同的語言下,一個意思的表達所使用的字符串長度是不一樣的惊科,這就影射出空間的寬度可能會不一樣拍摇。
好了,國際化的內(nèi)容就不說了馆截,請自行搜索充活。
1/**2*? 根據(jù)SCNetworkReachabilityFlags這個網(wǎng)絡(luò)標(biāo)記來轉(zhuǎn)換成我們在開發(fā)中經(jīng)常使用的網(wǎng)絡(luò)狀態(tài)31.不能連接網(wǎng)絡(luò)42.蜂窩連接53.WiFi連接64.未知連接7*/8staticAFNetworkReachabilityStatus AFNetworkReachabilityStatusForFlags(SCNetworkReachabilityFlags flags) {910//是否能夠到達11BOOL isReachable = ((flags & kSCNetworkReachabilityFlagsReachable) !=0);1213//在聯(lián)網(wǎng)之前需要建立連接14BOOL needsConnection = ((flags & kSCNetworkReachabilityFlagsConnectionRequired) !=0);1516//是否可以自動連接17BOOL canConnectionAutomatically = (((flags & kSCNetworkReachabilityFlagsConnectionOnDemand ) !=0) || ((flags & kSCNetworkReachabilityFlagsConnectionOnTraffic) !=0));1819//是否可以連接,在不需要用戶手動設(shè)置的前提下20BOOL canConnectWithoutUserInteraction = (canConnectionAutomatically && (flags & kSCNetworkReachabilityFlagsInterventionRequired) ==0);2122//是否可以聯(lián)網(wǎng)的條件 1.能夠到達 2.不需要建立連接或者不需要用戶手動設(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}
這個方法根據(jù)SCNetworkReachabilityFlags這個標(biāo)記轉(zhuǎn)換成我們自定義的枚舉類型蜡娶。至于轉(zhuǎn)換規(guī)則混卵,上邊的代碼中注釋部分寫的很清楚。
*** 在這里不得不多說幾句翎蹈,很多框架中都會把一個類中的私有方法寫成這樣。為什么呢男公? 我們在開發(fā)中經(jīng)常會寫成- (void)funcName; 這樣的私有方法荤堪。
我個人的意見是一個類中的私有方法寫成static void funcName() 這樣的c函數(shù)比較好合陵。
1. 在文件的最前方,比較容易查找
2. 可以適當(dāng)?shù)氖褂脙?nèi)聯(lián)函數(shù)澄阳,提高效率拥知。
根據(jù)一個標(biāo)識 來處理Block和通知。保證兩者同一狀態(tài)碎赢。
need-to-insert-img
包含了 類中需要處理的屬性低剔。
need-to-insert-img
來看這個最基本的初始化方法,初始化了自身的屬性肮塞。
CFRetain()后要記得CFRelease().
need-to-insert-img
通過一個socket地址來初始化襟齿。 首先新建SCNetworkReachabilityRef 對象,然后調(diào)用initWithReachability: 方法枕赵。記得手動管理內(nèi)存猜欺。
need-to-insert-img
這個方法基本同上。
綜合上邊兩個方法拷窜,我們發(fā)現(xiàn)?SCNetworkReachabilityRef 有兩個創(chuàng)建方法:
1.SCNetworkReachabilityCreateWithName
2.SCNetworkReachabilityCreateWithAddress
need-to-insert-img
由于IPv6 是ios9和os_x 10.11后邊推出的开皿,所有要進行版本判斷。這禮拜呢設(shè)計到的socket的知識篮昧,請看socket通信
通過這段代碼我們能學(xué)到什么呢赋荆?
1,方法的創(chuàng)建也是有順序的懊昨,可以使用函數(shù)訪問函數(shù)的思想窄潭。
2. @if 這樣的預(yù)編譯指令能夠替換掉代碼中部分if else 。好處就是代碼會不會被編譯的區(qū)別疚颊。
need-to-insert-img
單例的寫法狈孔。
need-to-insert-img
對需要釋放時,做一些處理材义。
need-to-insert-img
這個是.h文件暴露出來的3個BOOL 屬性的getter方法均抽,注意,由于我們在@property中定義了getter方法其掂,所以getter方法就要寫成我們定義的那種油挥。
從這3個方法中也能看出,函數(shù)嵌套的思想還是很重要款熬,要想做到這一點深寥,只能是多想才行。
need-to-insert-img
這個算是這個類的核心方法贤牛,設(shè)置監(jiān)聽網(wǎng)咯監(jiān)聽惋鹅。
我們先來了解下基礎(chǔ)知識。
SCNetworkReachabilityContext
點進去殉簸,會發(fā)現(xiàn)這是一個結(jié)構(gòu)體闰集,一般c語言的結(jié)構(gòu)體是對要保存的數(shù)據(jù)的一種描述
need-to-insert-img
1. 第一個參數(shù)接受一個signedlong 的參數(shù)
2. 第二個參數(shù)接受一個void * 類型的值沽讹,相當(dāng)于oc的id類型,void * 可以指向任何類型的參數(shù)
3. 第三個參數(shù) 是一個函數(shù) 目的是對info做retain操作武鲁,
4. 第四個參數(shù)是一個函數(shù)爽雄,目的是對info做release操作
5. 第五個參數(shù)是 一個函數(shù),根據(jù)info獲取Description字符串
在這里我們要攜帶的這個info就是下邊的這個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ù)是下邊的這兩個函數(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)控分為下邊幾個步驟:
1.我們先新建上下文
1SCNetworkReachabilityContext context = {0, (__bridgevoid*)callback, AFNetworkReachabilityRetainCallback, AFNetworkReachabilityReleaseCallback, NULL};
2.設(shè)置回調(diào)
1SCNetworkReachabilitySetCallback(self.networkReachability, AFNetworkReachabilityCallback, &context);
其中這個AFNetworkReachabilityCallback 是這樣被定義的一個函數(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
這兩個方法沒什么好說的了,一個是getter 一個是setter
need-to-insert-img
注冊鍵值依賴饲梭,這個可能大家平時用的比較少乘盖。可以了解一下
比如說一個類User中有兩個屬性
need-to-insert-img
還有一個卡片的類card
need-to-insert-img
我們寫一個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)聽card的info屬性,當(dāng)user中的name或者age的值發(fā)生改變的時候监氢,就會觸發(fā)info的鍵值監(jiān)聽方法布蔗。這就是鍵值依賴的作用。
好了 本篇文章就到此為止了浪腐。下一篇會是AFSecurityPolicy