AFNetworking 3.0 源碼閱讀- AFNetworkReachabilityManager

#import <SystemConfiguration/SystemConfiguration.h>
AFNetworkReachabilityManager是一個即插即用的模塊,對 SystemConfiguration模塊c函數(shù)的封裝,隱藏了 C 語言的實現(xiàn),提供了統(tǒng)一且簡潔的 Objective-C 語言接口。蘋果的文檔中也有一個類似的項目 Reachability
這里對網(wǎng)絡狀態(tài)的監(jiān)控跟蘋果官方的實現(xiàn)幾乎是完全相同的。

下面先從頭文件著手,之后再看類實現(xiàn)文件。

頭文件這里就簡單講幾點
1.NS_ASSUME_NONNULL_BEGIN和NS_ASSUME_NONNULL_END兩個宏
在swift中,可以使用!和?來表示一個對象是optional的還是non-optional,而在Objective-C中則沒有這一區(qū)分。這樣就會造成一個問題:在Swift與Objective-C混編時,Swift編譯器并不知道一個OC對象到底是optional還是non-optional,這種情況下編譯器會隱式地將OC的對象當成是non-optional。

為了解決這個問題,蘋果在Xcode 6.3引入了一個Objective-C的新特性:nullability annotations。這一新特性的核心是兩個新的類型注釋:__nullable__nonnull。__nullable表示對象可以是NULL或nil,而__nonnull表示對象不應該為空。不遵循這一規(guī)則時,編譯器就會給出警告。
__nullable__nonnull僅限于使用在指針類型上。而在方法的聲明中,我們還可以使用不帶下劃線的nullable和nonnull.

如果需要每個屬性或每個方法都去指定nonnull和nullable,是一件非常繁瑣的事。蘋果為了減輕我們的工作量,專門提供了兩個宏:NS_ASSUME_NONNULL_BEGINNS_ASSUME_NONNULL_END。在這兩個宏之間的代碼,所有簡單指針對象都被假定為nonnull,因此我們只需要去指定那些nullable的指針。

2.屬性的readonly和readwrite

.h
@property (readonly, nonatomic, assign) AFNetworkReachabilityStatus networkReachabilityStatus;
.m
@property (readwrite, nonatomic, assign) AFNetworkReachabilityStatus networkReachabilityStatus;

在.h和.m文件中networkReachabilityStatus屬性分別聲明為readonly、readwrite.在編程中,應該盡量把對外公布出來的屬性設置為只讀。如果有時候想修改封裝在對象內(nèi)部的數(shù)據(jù),但又不想讓這些數(shù)據(jù)被外人改動,這是可以在對象內(nèi)部把readonly屬性重新聲明為readwrite。

FOUNDATION_EXPORT#define都能定義常量。前者在檢測字符串的值是否相等的時候更快,對于第一種你可以直接使用stringInstance == MyFirstConstant來比較,而define則使用的是這種[stringInstance isEqualToString:MyFirstConstant]。第一種效率更高,第一種直接比較的是指針地址,而第二個則是一一比較字符串的每一個字符是否相等.
FOUNDATION_EXPORT一般是這樣使用的:

.h文件
FOUNDATION_EXPORT NSString * const AFNetworkingReachabilityDidChangeNotification;
FOUNDATION_EXPORT NSString * const AFNetworkingReachabilityNotificationStatusItem;
.m文件
//網(wǎng)絡狀態(tài)發(fā)生變化時接受的通知
NSString * const AFNetworkingReachabilityDidChangeNotification = @"com.alamofire.networking.reachability.change";
//網(wǎng)絡狀態(tài)發(fā)生變化時發(fā)送通知,攜帶的userInfo的key就是這個,value是代表AFNetworkReachabilityStatus的NSNumber
NSString * const AFNetworkingReachabilityNotificationStatusItem = @"AFNetworkingReachabilityNotificationStatusItem";
//回調(diào)

對這個類的.h文件就說得差不多了。接下來看類實現(xiàn)

AFNetworkReachabilityManager這個類是圍繞SCNetworkReachability來實現(xiàn)的,所以先講一點關(guān)于SCNetworkReachability的知識。

SCNetworkReachability

1.SCNetworkReachability 編程接口允許應用確定系統(tǒng)當前網(wǎng)絡配置的狀態(tài),還有目標主機的可達性。
當由應用發(fā)送到網(wǎng)絡堆棧的數(shù)據(jù)包可以離開本地設備的時候,遠程主機就可以被認為可以到達。 **可達性并不保證數(shù)據(jù)包一定會被主機接收到。 **

2、SCNetworkReachability編程接口支持同步和異步兩種模式。
在同步模式中,可以通過調(diào)用SCNetworkReachabilityGetFlag函數(shù)來獲得可達性狀態(tài);
在異步模式中,可以調(diào)度SCNetworkReachability對象到runloop,客戶端實現(xiàn)一個回調(diào)函數(shù)來接收通知,當可達性狀態(tài)改變就響應回調(diào)。

3.創(chuàng)建連接的引用
提供了幾個函數(shù)創(chuàng)建連接的引用SCNetworkReachabilityRef

根據(jù)傳入的域名創(chuàng)建網(wǎng)絡連接引用
SCNetworkReachabilityRef __nullable SCNetworkReachabilityCreateWithName     (
    CFAllocatorRef  __nullable  allocator,//NULL或者kCFAllocatorDefault
    const char      *nodename //域名
    )
根據(jù)傳入的地址創(chuàng)建網(wǎng)絡連接引用
SCNetworkReachabilityRef __nullable SCNetworkReachabilityCreateWithAddress(
    CFAllocatorRef          __nullable  allocator,
    const struct sockaddr       *address//期望連接的主機地址,當為0.0.0.0時則可以查詢本機的網(wǎng)絡連接狀態(tài)
    )

這些函數(shù)名中含有“Create”,因此函數(shù)返回的SCNetworkReachabilityRef引用必須調(diào)用CFRelease來釋放。
關(guān)于sockaddr:

struct sockaddr {
    __uint8_t   sa_len;       total length
 sa_family_t    sa_family;  協(xié)議族,一般都是“AF_xxx”的形式。通常大多用的是都是AF_INET,代表TCP/IP協(xié)議族
 char       sa_data[14];    14字節(jié)協(xié)議地址
 };
 這個數(shù)據(jù)結(jié)構(gòu)用做bind、connect、recvfrom、sendto等函數(shù)的參數(shù),指明地址信息。但一般編程中并不直接針對此數(shù)據(jù)結(jié)構(gòu)操作,而是使用sockaddr_in

4.獲取當前網(wǎng)絡狀態(tài)

Boolean SCNetworkReachabilityGetFlags (   //當前網(wǎng)絡配置下,目標是否可達,目標target就是參數(shù)1
   SCNetworkReachabilityRef target,            //之前建立的網(wǎng)絡連接的引用
   SCNetworkReachabilityFlags *flags           //保存獲得網(wǎng)絡狀態(tài)
);

初始化 AFNetworkReachabilityManager

1.+manager

+ (instancetype)manager
{
#if (defined(__IPHONE_OS_VERSION_MIN_REQUIRED) && __IPHONE_OS_VERSION_MIN_REQUIRED >= 90000) || (defined(__MAC_OS_X_VERSION_MIN_REQUIRED) && __MAC_OS_X_VERSION_MIN_REQUIRED >= 101100)
    struct sockaddr_in6 address;
    bzero(&address, sizeof(address));
    address.sin6_len = sizeof(address);
    address.sin6_family = AF_INET6;
#else
    struct sockaddr_in address;
    bzero(&address, sizeof(address));//初始化
    address.sin_len = sizeof(address);
    address.sin_family = AF_INET;
#endif
    return [self managerForAddress:&address];
}

使用這個類方法創(chuàng)建一個默認socket地址的AFNetworkReachabilityManager對象。
ipv6是iOS9和OSX10.11后推出的,因此這里要進行系統(tǒng)版本的判斷。

相關(guān)代碼講解:

 struct sockaddr_in {
 __uint8_t    sin_len;
 sa_family_t    sin_family; //協(xié)議族,在socket編程中只能是AF_INET
 in_port_t    sin_port;     //端口號(使用網(wǎng)絡字節(jié)順序)
 struct in_addr  sin_addr;  //按照網(wǎng)絡字節(jié)順序存儲IP地址,使用in_addr這個數(shù)據(jù)結(jié)構(gòu)
 char        sin_zero[8];   //讓sockaddr與sockaddr_in兩個數(shù)據(jù)結(jié)構(gòu)保持大小相同而保留的空字節(jié)。
//sockaddr_in和sockaddr是并列的結(jié)構(gòu),指向sockaddr_in的結(jié)構(gòu)體的指針也可以指向sockaddr的結(jié)構(gòu)體,并代替它。
//也就是說,你可以使用sockaddr_in建立你所需要的信息,然后用進行類型轉(zhuǎn)換就可以了
 };
 
 struct in_addr {
    in_addr_t s_addr;
 };
 結(jié)構(gòu)體in_addr 用來表示一個32位的IPv4地址。in_addr_t 是一個32位的unsigned long,其中每8位代表一個IP地址位中的一個數(shù)值。
   例如192.168.3.144記為0xc0a80390

2.+managerForAddress:和+managerForDomain:

+ (instancetype)managerForAddress:(const void *)address {
    //根據(jù)傳入的地址創(chuàng)建網(wǎng)絡連接引用。
    SCNetworkReachabilityRef reachability = SCNetworkReachabilityCreateWithAddress(kCFAllocatorDefault, (const struct sockaddr *)address);//返回的網(wǎng)絡連接引用必須在用完后釋放。
    AFNetworkReachabilityManager *manager = [[self alloc] initWithReachability:reachability];
    CFRelease(reachability);//手動管理內(nèi)存
    return manager;
}

+ (instancetype)managerForDomain:(NSString *)domain {
    //根據(jù)傳入的域名創(chuàng)建網(wǎng)絡連接引用
    SCNetworkReachabilityRef reachability = SCNetworkReachabilityCreateWithName(kCFAllocatorDefault, [domain UTF8String]);
    AFNetworkReachabilityManager *manager = [[self alloc] initWithReachability:reachability];
    CFRelease(reachability);//手動管理內(nèi)存
    return manager;
}

先使用 SCNetworkReachabilityCreateWithAddress或者 SCNetworkReachabilityCreateWithName 函數(shù)生成一個 SCNetworkReachabilityRef的引用。這里沒什么要說了,前面已經(jīng)簡單介紹過。
然后調(diào)用initWithReachability:方法如下:
3.-initWithReachability :

- (instancetype)initWithReachability:(SCNetworkReachabilityRef)reachability {
    self = [super init];
    if (!self) {
        return nil;
    }
    _networkReachability = CFRetain(reachability);//為什么要retain?誰創(chuàng)建誰釋放,這個參數(shù)reachability不是在這個方法中創(chuàng)建的。在+managerForDomain:和+managerForAddress:方法實現(xiàn)中,最后釋放掉了網(wǎng)絡連接引用reachability,因此要在本方法中先把它retain一次。
    self.networkReachabilityStatus = AFNetworkReachabilityStatusUnknown;
    return self;
}

讓_networkReachability持有 SCNetworkReachabilityRef的引用,并設置一個默認的網(wǎng)絡狀態(tài)。
為什么要retain這個SCNetworkReachabilityRef引用?個人理解:誰創(chuàng)建誰釋放,這個參數(shù)reachability在+managerForDomain:+managerForAddress:方法中創(chuàng)建也應由它們釋放。為了防止-initWithReachability:方法還沒執(zhí)行完,這個引用就已經(jīng)在+managerForDomain:+managerForAddress:釋放掉了,因此要在本方法中先把它retain一次。

dealloc中有對應的release。

4.-init
這個方法被直接禁用了。關(guān)于NS_UNAVAILABLE 可以看這篇文章

監(jiān)控網(wǎng)絡狀態(tài)

- (void)startMonitoring {
    [self stopMonitoring];//先關(guān)閉監(jiān)聽

    if (!self.networkReachability) {//如果網(wǎng)絡不可達,就返回
        return;
    }
    //避免循環(huán)引用要用weakself,避免在block執(zhí)行過程中,突然出現(xiàn)self被釋放的情況,就用strongself
    __weak __typeof(self)weakSelf = self;
    AFNetworkReachabilityStatusBlock callback = ^(AFNetworkReachabilityStatus status) {
        __strong __typeof(weakSelf)strongSelf = weakSelf;

        strongSelf.networkReachabilityStatus = status;
        if (strongSelf.networkReachabilityStatusBlock) {
            strongSelf.networkReachabilityStatusBlock(status);
        }

    };
    SCNetworkReachabilityContext context = {0, (__bridge void *)callback, AFNetworkReachabilityRetainCallback, AFNetworkReachabilityReleaseCallback, NULL};
    //設置回調(diào)。SCNetworkReachabilitySetCallback指定一個target(第一個參數(shù)),當設備對于這個target鏈接狀態(tài)發(fā)生改變時,就進行回調(diào)(第二個參數(shù))。它第二個參數(shù):SCNetworkReachabilityCallBack類型的值,是當網(wǎng)絡可達性更改時調(diào)用的函數(shù),如果為NULL,則目標的當前客戶端將被刪除。SCNetworkReachabilityCallBack中的info參數(shù)就是SCNetworkReachabilityContext中對應的那個info
    SCNetworkReachabilitySetCallback(self.networkReachability, AFNetworkReachabilityCallback, &context);
    //加入runloop
    SCNetworkReachabilityScheduleWithRunLoop(self.networkReachability, CFRunLoopGetMain(), kCFRunLoopCommonModes);
    //異步線程發(fā)送一次當前網(wǎng)絡狀態(tài)(通知)
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0),^{
        SCNetworkReachabilityFlags flags;
        //SCNetworkReachabilityGetFlags獲得可達性狀態(tài)
        if (SCNetworkReachabilityGetFlags(self.networkReachability, &flags)) {
            AFPostReachabilityStatusChange(flags, callback);
        }
    });
    /*SCNetworkReachability 編程接口支持同步和異步兩種模式。
    在同步模式中,可以通過調(diào)用SCNetworkReachabilityGetFlag函數(shù)來獲得可達性狀態(tài);
    在異步模式中,可以調(diào)度SCNetworkReachability對象到客戶端對象線程的運行循環(huán)上,客戶端實現(xiàn)一個回調(diào)函數(shù)來接收通知,當遠程主機改變可達性狀態(tài),回調(diào)則可響應。
    */
}

這算是這個類的核心方法。設置網(wǎng)絡監(jiān)控分為以下幾個步驟:
1.創(chuàng)建上下文。關(guān)于 SCNetworkReachabilityContext結(jié)構(gòu)體

typedef struct {
     CFIndex        version;   作為參數(shù)傳遞到SCDynamicStore創(chuàng)建函數(shù)的結(jié)構(gòu)類型的版本號,這個結(jié)構(gòu)體對應的是version 0。
     void *        __nullable info; 表示網(wǎng)絡狀態(tài)處理的回調(diào)函數(shù)。指向用戶指定的數(shù)據(jù)塊的C指針,void* 相當于oc的id
     const void    * __nonnull (* __nullable retain)(const void *info); retain info
     void        (* __nullable release)(const void *info); 對應上一個元素 release
     CFStringRef    __nonnull (* __nullable copyDescription)(const void *info); 提供信息字段的描述
     } SCNetworkReachabilityContext;

關(guān)于參數(shù)的說明:

  • CFIndex version:創(chuàng)建一個 SCNetworkReachabilityContext 結(jié)構(gòu)體時,需要調(diào)用 SCDynamicStore的創(chuàng)建函數(shù),SCNetworkReachabilityContext 對應的 version 是 0

  • void *__nullable info:表示網(wǎng)絡狀態(tài)處理的回調(diào)函數(shù)。指向用戶指定的數(shù)據(jù)塊的C指針,void* 相當于oc的id。
    要攜帶的這個info就是下面這個block,是一個在每次網(wǎng)絡狀態(tài)改變時的回調(diào)。而且block和void*的轉(zhuǎn)換不能直接轉(zhuǎn),要使用__bridge

__weak __typeof(self)weakSelf = self;
//1.網(wǎng)絡狀態(tài)變化時回調(diào)的是這個block
    AFNetworkReachabilityStatusBlock callback = ^(AFNetworkReachabilityStatus status) {
        __strong __typeof(weakSelf)strongSelf = weakSelf;
        strongSelf.networkReachabilityStatus = status;
//2.其中回調(diào)block中會執(zhí)行_networkReachabilityStatusBlock,這個block才是核心,由-setReachabilityStatusChangeBlock:方法對這個block進行設置
        if (strongSelf.networkReachabilityStatusBlock) {
            strongSelf.networkReachabilityStatusBlock(status);
        }
    };

避免循環(huán)引用要用weakself,避免在block執(zhí)行過程中,突然出現(xiàn)self被釋放的情況,就用strong self。傳送門

  • 第三第四個參數(shù):是一個函數(shù),分別對info retain和release
//調(diào)用了 Block_copy(用于 retain 一個 block 函數(shù),即在堆空間新建或直接引用一個 block 拷貝)
static const void * AFNetworkReachabilityRetainCallback(const void *info) {
    return Block_copy(info);
}
//調(diào)用了 Block_release(用于 release 一個 block 函數(shù),即將 block 從堆空間移除或移除相應引用)
static void AFNetworkReachabilityReleaseCallback(const void *info) {
    if (info) {
        Block_release(info);
    }
}
  • 第五個參數(shù):提供對info的一些說明描述

2.設置回調(diào)

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

函數(shù)的原型是這樣的:

Boolean SCNetworkReachabilitySetCallback(
    SCNetworkReachabilityRef    target,    //網(wǎng)絡連接引用
    SCNetworkReachabilityCallBack   __nullable  callout,//回調(diào)
    SCNetworkReachabilityContext    * __nullable    context//上下文
)

它的第二個參數(shù),SCNetworkReachabilityCallBack是這樣被定義的

typedef void (*SCNetworkReachabilityCallBack)(
    SCNetworkReachabilityRef    target,  //網(wǎng)絡連接引用
    SCNetworkReachabilityFlags  flags,   //狀態(tài)flag
    void    *   __nullable  info      //回調(diào),這個info和SCNetworkReachabilityContext中的info是同一個
    );

SCNetworkReachabilitySetCallback給當前客戶端指定一個目標target(第一個參數(shù),之前創(chuàng)建的網(wǎng)絡連接引用),當設備對于這個target連接狀態(tài)發(fā)生改變時,就進行回調(diào)(第二個參數(shù))。它第二個參數(shù):SCNetworkReachabilityCallBack類型的值,是當網(wǎng)絡可達性更改時調(diào)用的函數(shù),如果為NULL,則目標的當前客戶端將被刪除。SCNetworkReachabilityCallBack中的info參數(shù)就是SCNetworkReachabilityContext中對應的那個info。

在AF中,在每次網(wǎng)絡狀態(tài)改變,就會調(diào)用 AFNetworkReachabilityCallback 函數(shù):

static void AFNetworkReachabilityCallback(SCNetworkReachabilityRef __unused target, SCNetworkReachabilityFlags flags, void *info) {
    AFPostReachabilityStatusChange(flags, (__bridge AFNetworkReachabilityStatusBlock)info);//這里void*和id的轉(zhuǎn)換,在ARC下要加__bridge修飾
}

這里會從 info 中取出之前存在 context 中的 AFNetworkReachabilityStatusBlock 回調(diào)block,并把這個block傳遞給 AFPostReachabilityStatusChange 函數(shù):

//根據(jù)flag來獲得對應的網(wǎng)絡狀態(tài),在主線程中進行對應的回調(diào)(block),發(fā)送通知。
//根據(jù)同一個status來處理block 和通知,封裝到一個函數(shù)中保持兩者統(tǒng)一
static void AFPostReachabilityStatusChange(SCNetworkReachabilityFlags flags, AFNetworkReachabilityStatusBlock block) {
    AFNetworkReachabilityStatus status = AFNetworkReachabilityStatusForFlags(flags);
    dispatch_async(dispatch_get_main_queue(), ^{
        if (block) {
            block(status);
        }
        NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter];
        NSDictionary *userInfo = @{ AFNetworkingReachabilityNotificationStatusItem: @(status) };
        [notificationCenter postNotificationName:AFNetworkingReachabilityDidChangeNotification object:nil userInfo:userInfo];
    });
}
  • 調(diào)用 AFNetworkReachabilityStatusForFlags 獲取當前的網(wǎng)絡可達性狀態(tài)
  • 在主線程中異步執(zhí)行上面?zhèn)魅氲?callback block(設置 self 的網(wǎng)絡狀態(tài),調(diào)用 networkReachabilityStatusBlock
__weak __typeof(self)weakSelf = self;
  AFNetworkReachabilityStatusBlock callback = ^(AFNetworkReachabilityStatus status) {
      __strong __typeof(weakSelf)strongSelf = weakSelf;
      strongSelf.networkReachabilityStatus = status;
      if (strongSelf.networkReachabilityStatusBlock) {
          strongSelf.networkReachabilityStatusBlock(status);
      }
  };
  • 發(fā)送 AFNetworkingReachabilityDidChangeNotification 通知.

3.加入runloop池
在 Main Runloop 中對應的模式開始監(jiān)控網(wǎng)絡狀態(tài)

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

4.獲取當前的網(wǎng)絡狀態(tài),調(diào)用 callback

    //子線程中獲取網(wǎng)絡狀態(tài),主線程執(zhí)行回調(diào)并發(fā)送通知
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0),^{
        SCNetworkReachabilityFlags flags;
        //SCNetworkReachabilityGetFlags獲得可達性狀態(tài)
        if (SCNetworkReachabilityGetFlags(self.networkReachability, &flags)) {
            AFPostReachabilityStatusChange(flags, callback);
        }
    });

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

使用 SCNetworkReachabilityUnscheduleFromRunLoop 方法取消之前在 Main Runloop 中的監(jiān)聽

- (void)stopMonitoring {
    if (!self.networkReachability) {
        return;
    }
    SCNetworkReachabilityUnscheduleFromRunLoop(self.networkReachability, CFRunLoopGetMain(), kCFRunLoopCommonModes);
}

其他

1.設置networkReachabilityStatusBlock

每次網(wǎng)絡狀態(tài)改變時, 實際上調(diào)用的是這個block

- (void)setReachabilityStatusChangeBlock:(void (^)(AFNetworkReachabilityStatus status))block {
    self.networkReachabilityStatusBlock = block;
}

在每次網(wǎng)絡狀態(tài)改變時, 調(diào)用的是定義在-startMonitoring中的AFNetworkReachabilityStatusBlock callback這個block,而這個block會執(zhí)行_networkReachabilityStatusBlock。這個block需要人為設置。

2.根據(jù)SCNetworkReachabilityFlags轉(zhuǎn)換成開發(fā)中使用的AFNetworkReachabilityStatus網(wǎng)絡狀態(tài)

static AFNetworkReachabilityStatus AFNetworkReachabilityStatusForFlags(SCNetworkReachabilityFlags flags) {
//&按位與運算 和&&不一樣
    // 該網(wǎng)絡地址可達
    BOOL isReachable = ((flags & kSCNetworkReachabilityFlagsReachable) != 0);
    // 該網(wǎng)絡地址雖然可達,但是需要先建立一個 connection
    BOOL needsConnection = ((flags & kSCNetworkReachabilityFlagsConnectionRequired) != 0);
    // 該網(wǎng)絡雖然也需要先建立一個 connection,但是它是可以自動去 connect 的
    BOOL canConnectionAutomatically = (((flags & kSCNetworkReachabilityFlagsConnectionOnDemand ) != 0) || ((flags & kSCNetworkReachabilityFlagsConnectionOnTraffic) != 0));
    // 不需要用戶交互,就可以 connect 上(用戶交互一般指的是提供網(wǎng)絡的賬戶和密碼)
    BOOL canConnectWithoutUserInteraction = (canConnectionAutomatically && (flags & kSCNetworkReachabilityFlagsInterventionRequired) == 0);
    // 如果 isReachable==YES,那么就需要判斷是不是得先建立一個 connection,如果需要,那就認為不可達,或者雖然需要先建立一個 connection,但是不需要用戶交互,那么認為也是可達的
    BOOL isNetworkReachable = (isReachable && (!needsConnection || canConnectWithoutUserInteraction));
    
    //  AFNetworkReachabilityStatus 就四種狀態(tài) Unknown、NotReachable、ReachableViaWWAN、ReachableViaWiFi,這四種狀態(tài)字面意思很好理解,這里就不贅述了
    AFNetworkReachabilityStatus status = AFNetworkReachabilityStatusUnknown;
    if (isNetworkReachable == NO) {
        status = AFNetworkReachabilityStatusNotReachable;
    }
#if TARGET_OS_IPHONE
    else if ((flags & kSCNetworkReachabilityFlagsIsWWAN) != 0) {
        status = AFNetworkReachabilityStatusReachableViaWWAN;
    }
#endif
    else {
        status = AFNetworkReachabilityStatusReachableViaWiFi;
    }

    return status;
}

因為 flags 是一個SCNetworkReachabilityFlags,它的不同位代表了不同的網(wǎng)絡可達性狀態(tài),通過 flags 的位操作,獲取當前的狀態(tài)信息 AFNetworkReachabilityStatus。

3.本地化

NSString * AFStringFromNetworkReachabilityStatus(AFNetworkReachabilityStatus status) {
    switch (status) {
        case AFNetworkReachabilityStatusNotReachable:
            return NSLocalizedStringFromTable(@"Not Reachable", @"AFNetworking", nil);
        case AFNetworkReachabilityStatusReachableViaWWAN:
            return NSLocalizedStringFromTable(@"Reachable via WWAN", @"AFNetworking", nil);
        case AFNetworkReachabilityStatusReachableViaWiFi:
            return NSLocalizedStringFromTable(@"Reachable via WiFi", @"AFNetworking", nil);
        case AFNetworkReachabilityStatusUnknown:
        default:
            return NSLocalizedStringFromTable(@"Unknown", @"AFNetworking", nil);
    }
}

這是一個函數(shù)的實現(xiàn) 根據(jù)AFNetworkReachabilityStatus獲取本地化字符串。NSLocalizedStringFromTable用于本地化。

- (NSString *)localizedNetworkReachabilityStatusString {
    return AFStringFromNetworkReachabilityStatus(self.networkReachabilityStatus);
}

對上面的函數(shù)進行oc方法封裝,返回一個網(wǎng)絡狀態(tài)的本地語言字符串。通過這個字符串可以告訴用戶當前網(wǎng)絡發(fā)生什么?;蛘吒鶕?jù)返回的狀態(tài)自定義提示文字。

4.注冊鍵值依賴

+ (NSSet *)keyPathsForValuesAffectingValueForKey:(NSString *)key {
    if ([key isEqualToString:@"reachable"] || [key isEqualToString:@"reachableViaWWAN"] || [key isEqualToString:@"reachableViaWiFi"]) {
        return [NSSet setWithObject:@"networkReachabilityStatus"];
    }
    return [super keyPathsForValuesAffectingValueForKey:key];
}

和KVO有關(guān)的鍵值依賴。監(jiān)聽reachable、reachableViaWWAN、reachableViaWiFi屬性,當networkReachabilityStatus屬性值發(fā)生變化就會觸發(fā)那三個屬性的鍵值監(jiān)聽方法。

5.AF中對私有“方法”的寫法

AF中把私有方法寫成c函數(shù)的形式static void funcName(),比如:

NSString * AFStringFromNetworkReachabilityStatus(AFNetworkReachabilityStatus status) {...}
static AFNetworkReachabilityStatus AFNetworkReachabilityStatusForFlags(SCNetworkReachabilityFlags flags) {...}
static void AFPostReachabilityStatusChange(SCNetworkReachabilityFlags flags, AFNetworkReachabilityStatusBlock block){...}

這些私有函數(shù)放到類文件頭部,你即使不看這些代碼,對于整個業(yè)務的理解也不會受到影響。所以,這種寫法值得推薦??梢赃m當?shù)氖褂脙?nèi)聯(lián)函數(shù),提高效率

使用

1.初始化 AFNetworkReachabilityManage
2.調(diào)用 startMonitoring方法開始對網(wǎng)絡狀態(tài)進行監(jiān)
3.設置 networkReachabilityStatusBlock在每次網(wǎng)絡狀態(tài)改變時, 調(diào)用這個 block

詳細源碼注釋請戳:https://github.com/huixinHu/AFNetworking-

參考文章
Block內(nèi)存管理實例分析
IOS SCNetworkReachability和Reachability監(jiān)測網(wǎng)絡連接狀態(tài)
sockaddr和sockaddr_in的區(qū)別
NS_UNAVAILABLE 與 NS_DESIGNATED_INITIALIZER
AFNetworking 3.0 源碼解讀(一)之 AFNetworkReachabilityManager

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

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

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