AfNetworking 3.0源碼解讀

作ios開發,AFNetworking 這個網絡框架確定都很是熟悉,也許咱們平時只使用了它的部分功能,並且咱們對它的實現原理並非很清楚,就好像老是有一團迷霧在眼前同樣。html

接下來咱們就很是詳細的來讀一讀這個框架的代碼,咱們的目標就是理解了它的思想以後,可以明白咱們的請求是如何實現的,咱們的代碼哪裏還須要進行改進,若是可以更進一步,咱們可以總結出一套適合大部分應用的網絡架構思想。ios

可以讓一些人從中受益。編程

咱們先來看看整個框架的文件系統,咱們先不對每一個文件的做用進行說明,在整個源碼解讀最後的一篇中咱們會對整個框架進行總結。會有一張清晰的圖表來講明這個問題。swift

咱們在看一個框架的時候呢,能夠這樣先看,先看每一個文件的頭文件,也就是.h文件api

能夠看到,有的頭文件是包含了別的頭文件的,在不考慮系統的頭文件的狀況下,咱們可以發現一些比較獨立的類,從上圖中,咱們能夠看出網絡

比較獨立的類有:架構

1.AFURLResponseSerialization.happ

2.AFNetworkReachabilityManager.h框架

3.AFURLRequestSerialization.hdom

4.AFSecurityPolicy.h

本篇就介紹AFNetworkReachabilityManager.h的內容,這個是用來監控網絡環境變化的類。

 

#import <SystemConfiguration/SystemConfiguration.h>

經過導入了這個頭文件,咱們得知:網絡監控的實現是依賴SystemConfiguration這個api的。說明這個api可以提供這樣的功能,至少讓咱們明白了咱們平時都會導入它的一個用途。

這是一個枚舉封裝,仍是遵循一個使用枚舉的原則,當知足一個有限的並具備統一主題的集合的時候,咱們就考慮枚舉。在這裏做者是枚舉了4種類型。這幾種類型可以知足咱們開發中大部分的功能,若是不知足,能夠自行進行擴展。

NS_ASSUME_NONNULL_BEGIN
NS_ASSUME_NONNULL_END

這個是爲了swift的可選類型配添加的,在這兩個終點的內容的參數默認都是nonnull的。

這段文字是對這個類的說明。咱們估且不去管它說了什麼,在看看蘋果官方的

*** 這樣的內容會出如今一個屬性或者方法的上方,目的是對其內容的解釋。我看到這裏就想到了咱們平時的開發,咱們可以把每段代碼都當成是api的開發,也把註釋寫的詳細一點。曾經看過兩種不一樣的說辭,一種是說把代碼註釋儘可能少些,要求代碼簡介可讀性強。另外一種是說註釋要詳細,着重考慮他人讀代碼的感覺。我的感受仍是寫詳細一點比較好,由於可能過一段時間以後,本身再去看本身當時寫的代碼可能就不記得了。頗有可能在寫這些繁瑣的註釋的過程當中,可以想到些什麼,好比如何合併掉一些不必的方法等等。

本類提供了四個只讀的屬性來讓咱們獲取咱們須要的內容

1. 網絡狀態

2. 是不是可達的

3. 當前鏈接是不是WWAN

4. 當前鏈接是夠是WiFi

四個屬性均爲只讀屬性,只給了用戶訪問權,注意BOOL屬性通常是要寫getter方法的。

做者使用了這個來分隔同一類中不一樣功能模塊。這個算是我的習慣問題吧。舉個平時開發的例子,在.m文件中我我的使用#pragma mark 分隔不一樣功能。

提供了5中初始化方法,可以知足大部分的需求。

SCNetworkReachabilityRef 這個很重要,這個類的就是基於它開發的。

+ (instancetype)managerForDomain:(NSString *)domain; 監聽制定domain的網絡狀態。

+ (instancetype)managerForAddress:(const void *)address; 監聽某個socket地址的網絡狀態,socket通訊請看這篇文章: socket通訊

 

打開和關閉監聽的方法。

返回一個網絡狀態的本地語言的字符串。每每咱們能夠根據這個字符串來告訴用戶,當前網絡發生了什麼,固然,也能夠根據狀態自定義提示文字。

設置網絡轉態改變的回調,監聽網絡改變的回調有兩種方式:

1.使用上邊的這個方法。

2.監聽AFNetworkingReachabilityDidChangeNotification通知。

 

這個是與網絡狀態變化相關的通知。接受的通知中會有一個userinfo 是一個NSDictionary 其中key就是

AFNetworkingReachabilityNotificationStatusItem 

*** 這簡單的兩行代碼可以告訴咱們的是,咱們平時的開發中 但凡設計到發通知的功能,咱們應該把通知的字符串封裝到一個專有的文件中,同時在文件內部按不一樣模塊進行區分,固然必要的註釋也頗有必要。

ps: FOUNDATION_EXPORT 和#define 都能定義常量。FOUNDATION_EXPORT 可以使用==進行判斷,效率略高。並且可以隱藏定義細節(就是實現部分不在.中)

 

對函數:根據狀態獲取字符串  聲明。

好了,這個類的.h文件咱們已經很是相信的進行解讀了,咱們並非大概的說了下他提供的功能,而是經過讀每行代碼,咱們能聯想到什麼,什麼東西能幫助咱們更好的編程。

咱們接着看 AFNetworkReachabilityManager.m 的內容

 

這幾個頭文件是系統庫,是爲了後邊的 sockaddr_in6 / sockaddr_in 準備的,不熟悉的能夠看這篇文章 socket通訊

 

這幾個就沒什麼好說的了,咱們接着看

這個方法是對.h 中最後一個方法的實現。指的咱們注意的是NSLocalizedStringFromTable這個宏。爲何要注意它呢?

這就涉及到本地國際化的問題。所謂的國際化就是讓你的app可以根據不一樣的語言顯示相對應的語言。

*** 但這並不簡單,沒有經驗的開發人員,一開始可能不會作這樣的設置,若是往後須要國際話了,在作就很麻煩了。因此說在開中,但凡使用到字符串的地方都要考慮語言的不一樣。不一樣的語言下,一個意思的表達所使用的字符串長度是不同的,這就影射出空間的寬度可能會不同。

好了,國際化的內容就不說了,請自行搜索。

複製代碼
 1 /**
 2  *  根據SCNetworkReachabilityFlags這個網絡標記來轉換成咱們在開發中常用的網絡狀態
 3      1.不能鏈接網絡
 4      2.蜂窩鏈接
 5      3.WiFi鏈接
 6      4.未知鏈接
 7  */
 8 static AFNetworkReachabilityStatus AFNetworkReachabilityStatusForFlags(SCNetworkReachabilityFlags flags) {
 9     
10     // 是否可以到達
11     BOOL isReachable = ((flags & kSCNetworkReachabilityFlagsReachable) != 0);
12     
13     // 在聯網以前須要創建鏈接
14     BOOL needsConnection = ((flags & kSCNetworkReachabilityFlagsConnectionRequired) != 0);
15     
16     // 是否能夠自動鏈接
17     BOOL canConnectionAutomatically = (((flags & kSCNetworkReachabilityFlagsConnectionOnDemand ) != 0) || ((flags & kSCNetworkReachabilityFlagsConnectionOnTraffic) != 0));
18     
19     // 是否能夠鏈接,在不須要用戶手動設置的前提下
20     BOOL canConnectWithoutUserInteraction = (canConnectionAutomatically && (flags & kSCNetworkReachabilityFlagsInterventionRequired) == 0);
21     
22     // 是否能夠聯網的條件 1.可以到達 2.不須要創建鏈接或者不須要用戶手動設置鏈接 就表示可以鏈接到網絡
23     BOOL isNetworkReachable = (isReachable && (!needsConnection || canConnectWithoutUserInteraction));
24 
25     AFNetworkReachabilityStatus status = AFNetworkReachabilityStatusUnknown;
26     if (isNetworkReachable == NO) {
27         status = AFNetworkReachabilityStatusNotReachable;
28     }
29 #if    TARGET_OS_IPHONE
30     else if ((flags & kSCNetworkReachabilityFlagsIsWWAN) != 0) {
31         status = AFNetworkReachabilityStatusReachableViaWWAN;
32     }
33 #endif
34     else {
35         status = AFNetworkReachabilityStatusReachableViaWiFi;
36     }
37 
38     return status;
39 }
複製代碼

這個方法根據SCNetworkReachabilityFlags這個標記轉換成咱們自定義的枚舉類型。至於轉換規則,上邊的代碼中註釋部分寫的很清楚。

*** 在這裏不得很少說幾句,不少框架中都會把一個類中的私有方法寫成這樣。爲何呢? 咱們在開發中常常會寫成- (void)funcName; 這樣的私有方法。

我我的的意見是一個類中的私有方法寫成static void funcName() 這樣的c函數比較好。 

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

2. 能夠適當的使用內聯函數,提升效率。

 

根據一個標識 來處理Block和通知。保證二者同一狀態。

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

來看這個最基本的初始化方法,初始化了自身的屬性。

CFRetain()後要記得CFRelease().

經過一個socket地址來初始化。 首先新建 SCNetworkReachabilityRef 對象,而後調用initWithReachability: 方法。記得手動管理內存。

 

這個方法基本同上。

綜合上邊兩個方法,咱們發現 SCNetworkReachabilityRef 有兩個建立方法:

1. SCNetworkReachabilityCreateWithName 

2. SCNetworkReachabilityCreateWithAddress

 

因爲IPv6 是ios9和os_x 10.11後邊推出的,全部要進行版本判斷。這禮拜呢設計到的socket的知識,請看 socket通訊

經過這段代碼咱們能學到什麼呢?

1,方法的建立也是有順序的,可使用函數訪問函數的思想。

2. @if 這樣的預編譯指令可以替換掉代碼中部分if else 。好處就是代碼會不會被編譯的區別。

單例的寫法。

對須要釋放時,作一些處理。

這個是.h文件暴露出來的3個BOOL 屬性的getter方法,注意,因爲咱們在@property中定義了getter方法,因此getter方法就要寫成咱們定義的那種。

從這3個方法中也能看出,函數嵌套的思想仍是很重要,要想作到這一點,只能是多想才行。

這個算是這個類的核心方法,設置監聽網咯監聽。

咱們先來了解下基礎知識。

SCNetworkReachabilityContext

點進去,會發現這是一個結構體,通常c語言的結構體是對要保存的數據的一種描述

 

1. 第一個參數接受一個signed long 的參數

2. 第二個參數接受一個void * 類型的值,至關於oc的id類型,void * 能夠指向任何類型的參數

3. 第三個參數 是一個函數 目的是對info作retain操做,

4. 第四個參數是一個函數,目的是對info作release操做

5. 第五個參數是 一個函數,根據info獲取Description字符串

在這裏咱們要攜帶的這個info就是下邊的這個block

複製代碼
 1 __weak __typeof(self)weakSelf = self;
 2     AFNetworkReachabilityStatusBlock callback = ^(AFNetworkReachabilityStatus status) {
 3         __strong __typeof(weakSelf)strongSelf = weakSelf;
 4 
 5         strongSelf.networkReachabilityStatus = status;
 6         if (strongSelf.networkReachabilityStatusBlock) {
 7             strongSelf.networkReachabilityStatusBlock(status);
 8         }
 9 
10     };
複製代碼

retain和release 函數是下邊的這兩個函數

複製代碼
1 static const void * AFNetworkReachabilityRetainCallback(const void *info) {
2     return Block_copy(info);
3 }
4 
5 static void AFNetworkReachabilityReleaseCallback(const void *info) {
6     if (info) {
7         Block_release(info);
8     }
9 }
複製代碼

設置網絡監控分爲下邊幾個步驟:

1.咱們先新建上下文

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

2.設置回調

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

其中這個AFNetworkReachabilityCallback 是這樣被定義的一個函數

typedef void (*SCNetworkReachabilityCallBack)    (
                        SCNetworkReachabilityRef            target,
                        SCNetworkReachabilityFlags            flags,
                        void                 *    __nullable    info
                        );

在本類中

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

3.加入RunLoop池

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

其中CFRunLoopGetMain()表明主RunLoop

ok,差很少已經完成

 

在異步線程 發送一次當前的網絡狀態。

 

中止網絡監控

這兩個方法沒什麼好說的了,一個是getter 一個是setter 

註冊鍵值依賴,這個可能你們平時用的比較少。能夠了解一下

好比說一個類User中有兩個屬性

 

還有一個卡片的類card

 咱們寫一個info的setter 和 getter  方法,

複製代碼
 1 @interface User :NSObject
 2 @property (nonatomic,copy)NSString *name;
 3 @property (nonatomic,assign)NSUInteger age;
 4 @end
 5 
 6 
 7 
 8 @interface card :NSObject
 9 @property (nonatomic,copy)NSString *info;
10 @property (nonatomic,strong)User *user;
11 @end
12 @implementation card
13 
14 - (NSString *)info {
15     return [NSString stringWithFormat:@"%@/%lu",_user.name,(unsigned long)_user.age];
16 }
17 - (void)setInfo:(NSString *)info {
18     
19     NSArray *array = [info componentsSeparatedByString:@"/"];
20     _user.name = array[0];
21     _user.age = [array[1] integerValue];
22     
23 }
24 
25 + (NSSet<NSString *> *)keyPathsForValuesAffectingValueForKey:(NSString *)key {
26     NSSet * keyPaths = [super keyPathsForValuesAffectingValueForKey:key];
27     NSArray * moreKeyPaths = nil;
28 
29     if ([key isEqualToString:@"info"])
30     {
31         moreKeyPaths = [NSArray arrayWithObjects:@"user.name", @"user.age", nil];
32     }
33 
34     if (moreKeyPaths)
35     {
36         keyPaths = [keyPaths setByAddingObjectsFromArray:moreKeyPaths];
37     }
38     
39     return keyPaths;
40 }
41 
42 @end
複製代碼

代碼差很少就是上邊的。咱們能夠監聽card的info屬性,當user中的name或者age的值發生改變的時候,就會觸發info的鍵值監聽方法。這就是鍵值依賴的做用。

相關文章
相關標籤/搜索