#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_BEGIN和NS_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 是 0void *__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