AFNetworking中提供了AFNetworkReachabilityManager類,用於應用生命週期中網路狀態的監聽。
AFNetworkReachabilityManager繼承自NSObject
@interface AFNetworkReachabilityManager : NSObject
AFNetworkReachabilityManager定義了一個枚舉,專門用來表示網路所處的各種狀態
typedef NS_ENUM(NSInteger, AFNetworkReachabilityStatus) { AFNetworkReachabilityStatusUnknown = -1, AFNetworkReachabilityStatusNotReachable = 0, AFNetworkReachabilityStatusReachableViaWWAN = 1, AFNetworkReachabilityStatusReachableViaWiFi = 2,};
AFNetworkReachabilityStatusUnknown 表示未知網路
AFNetworkReachabilityStatusNotReachable 表示網路未串連
AFNetworkReachabilityStatusReachableViaWWAN 表示是行動電話通訊,2G,3G,4G
AFNetworkReachabilityStatusReachableViaWiFi 表示串連的WiFi
@property (readonly, nonatomic, assign) AFNetworkReachabilityStatus networkReachabilityStatus;
networkReachabilityStatus屬性工作表示當前應用所處的網路狀態
@property (readonly, nonatomic, assign, getter = isReachable) BOOL reachable;
reachable 屬性工作表示當前應用是否串連了網路,這個屬性的getter方法被重新自訂過,如果應用處於行動電話通訊(WWAN)或者WiFi狀態下,就返回YES,否則返回NO,如下
- (BOOL)isReachable { return [self isReachableViaWWAN] || [self isReachableViaWiFi];}
@property (readonly, nonatomic, assign, getter = isReachableViaWWAN) BOOL reachableViaWWAN;
reachableViaWWAN 屬性工作表示應用當前是否處於WWAN網路狀態下,如果在該網路環境下,就返回YES,否則返回NO
@property (readonly, nonatomic, assign, getter = isReachableViaWiFi) BOOL reachableViaWiFi;
reachableViaWiFi 屬性工作表示應用當前是否處於WiFi網路狀態下,如果應用處於WiFi環境下,就返回YES,否則返回NO
屬性就講到這裡,訊息我們就去.m檔案中看看。
--------------------------------------------------------------------分割線,棒棒噠--------------------------------------------------------------------------------
@interface AFNetworkReachabilityManager ()@property (readonly, nonatomic, assign) SCNetworkReachabilityRef networkReachability;@property (readwrite, nonatomic, assign) AFNetworkReachabilityStatus networkReachabilityStatus;@property (readwrite, nonatomic, copy) AFNetworkReachabilityStatusBlock networkReachabilityStatusBlock;@end
SCNetworkReachabilityRef networkReachability; 屬性
AFNetworkReachabilityStatus networkReachabilityStatus; 屬性
AFNetworkReachabilityStatusBlock networkReachabilityStatusBlock; 屬性,這是一個block屬性,用來做網路狀態串連的時候,回調這個block
+ (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];}
來分析下+(instancetype)manager類方法:
先先行編譯,根據__IPHONE_OS_VERSION_MIN_REQUIRED(ios版本號碼) 高於 9.0,以及__MAC_OS_X_VERSION_MIN_REQUIRED (mac版本號碼) 高於 10.11,那麼我們就必須要使用ipv6的方式,如果低於這兩個系統的版本號碼,我們就用ipv4的方法來建立socket通訊端。
+ (instancetype)managerForAddress:(const void *)address { SCNetworkReachabilityRef reachability = SCNetworkReachabilityCreateWithAddress(kCFAllocatorDefault, (const struct sockaddr *)address); AFNetworkReachabilityManager *manager = [[self alloc] initWithReachability:reachability]; CFRelease(reachability); return manager;}
managerForAddress這個類方法是根據傳入的通訊端來建立一個網路連接引用。
SCNetworkReachabilityRef reachability = SCNetworkReachabilityCreateWithAddress(kCFAllocatorDefault,(const struct sockaddr *)address);
這個c方法就是根據傳入的參數來擷取網路連接的一個引用,第一個參數可以是NULL或kCFAllocatorDefault,第二個參數就是需要測試連接的IP地址,建立好網路連接引用SCNetworkReachabilityRef使用後,注意要使用CFRelease來釋放掉這個資料
上邊提到用通訊端作為一個網路連接引用建立的時候的參數,同樣,我們可以不用通訊端IP地址去做參數,我們可以提供一個網域名稱來建立網路連接引用
+ (instancetype)managerForDomain:(NSString *)domain { SCNetworkReachabilityRef reachability = SCNetworkReachabilityCreateWithName(kCFAllocatorDefault, [domain UTF8String]); AFNetworkReachabilityManager *manager = [[self alloc] initWithReachability:reachability]; CFRelease(reachability); return manager;}
- (instancetype)initWithReachability:(SCNetworkReachabilityRef)reachability { self = [super init]; if (!self) { return nil; } _networkReachability = CFRetain(reachability); self.networkReachabilityStatus = AFNetworkReachabilityStatusUnknown; return self;}
一旦我們有了SCNetworkReachabilityRef網路連接的引用,那麼我們就可以使用這個串連引用去初始化我們的屬性_networkReachability,並且在初始化的時候我們先將 self.networkReachabilityStatus 設定為未知網路狀態。
+ (instancetype)sharedManager { static AFNetworkReachabilityManager *_sharedManager = nil; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ _sharedManager = [self manager]; }); return _sharedManager;}
sharedManager方法就是使用manager來得到AFNetworkReachabilityManager單例
- (void)dealloc { [self stopMonitoring]; if (_networkReachability != NULL) { CFRelease(_networkReachability); }}
解構函式,裡邊的操作當然得釋放資源了
- (BOOL)isReachable { return [self isReachableViaWWAN] || [self isReachableViaWiFi];}- (BOOL)isReachableViaWWAN { return self.networkReachabilityStatus == AFNetworkReachabilityStatusReachableViaWWAN;}- (BOOL)isReachableViaWiFi { return self.networkReachabilityStatus == AFNetworkReachabilityStatusReachableViaWiFi;}
返回屬性,已判斷應用所處網路狀態
- (void)startMonitoring { [self stopMonitoring]; if (!self.networkReachability) { return; } __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}; SCNetworkReachabilitySetCallback(self.networkReachability, AFNetworkReachabilityCallback, &context); SCNetworkReachabilityScheduleWithRunLoop(self.networkReachability, CFRunLoopGetMain(), kCFRunLoopCommonModes); dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0),^{ SCNetworkReachabilityFlags flags; if (SCNetworkReachabilityGetFlags(self.networkReachability, &flags)) { AFPostReachabilityStatusChange(flags, callback); } });}
我們來重點看看這個方法實現:
__weak __typeof(self)weakSelf = self; AFNetworkReachabilityStatusBlock callback = ^(AFNetworkReachabilityStatus status) { __strong __typeof(weakSelf)strongSelf = weakSelf; strongSelf.networkReachabilityStatus = status; if (strongSelf.networkReachabilityStatusBlock) { strongSelf.networkReachabilityStatusBlock(status); } };
這裡我們建立了一個AFNetworkReachabilityStatusBlock類型的block,在這個block裡邊我們執行了我們的屬性block,也就是說,一旦回調callback這個block,那麼相應的我們自訂的AFNetworkReachabilityStatusBlock的屬性_networkReachabilityStatusBlock也會被回調。
SCNetworkReachabilityContext context = {0, (__bridge void *)callback, AFNetworkReachabilityRetainCallback, AFNetworkReachabilityReleaseCallback, NULL};
建立SCNetworkReachabilityContext context,介紹下SCNetworkReachabilityContext結構內容
typedef struct { CFIndex version; void * info; const void *(*retain)(const void *info); void (*release)(const void *info); CFStringRef (*copyDescription)(const void *info);} SCNetworkReachabilityContext;
這個結構體中涉及到的幾個參數
CFIndex version; 0
void * info; 就是上邊建立的callback
const void * (*retain)(const void * info); 返回任意指標類型,參數是一個指標,的指標函數
static const void * AFNetworkReachabilityRetainCallback(const void *info) { return Block_copy(info);}
void (*release)(const void * info);
static void AFNetworkReachabilityReleaseCallback(const void *info) { if (info) { Block_release(info); }}
CFStringRef (*copyDescription) (const void *info); NULL
SCNetworkReachabilitySetCallback(self.networkReachability, AFNetworkReachabilityCallback, &context);
試著給self.networkReachability這個網路連接引用設定回調方法,如果self.networkReachability這個網路連接引用發生改變,那麼AFNetworkReachabilityCallBack就會回調
static void AFNetworkReachabilityCallback(SCNetworkReachabilityRef __unused target, SCNetworkReachabilityFlags flags, void *info) { AFPostReachabilityStatusChange(flags, (__bridge AFNetworkReachabilityStatusBlock)info);}
一旦self.networkReachability發生改變,上邊的方法就會被調用,所以AFPostReachabilityStatusChange();方法就會被調用
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]; });}
然後根據flags得到網路連接狀態AFNetworkReachabilityStatus
static AFNetworkReachabilityStatus AFNetworkReachabilityStatusForFlags(SCNetworkReachabilityFlags flags) { BOOL isReachable = ((flags & kSCNetworkReachabilityFlagsReachable) != 0); BOOL needsConnection = ((flags & kSCNetworkReachabilityFlagsConnectionRequired) != 0); BOOL canConnectionAutomatically = (((flags & kSCNetworkReachabilityFlagsConnectionOnDemand ) != 0) || ((flags & kSCNetworkReachabilityFlagsConnectionOnTraffic) != 0)); BOOL canConnectWithoutUserInteraction = (canConnectionAutomatically && (flags & kSCNetworkReachabilityFlagsInterventionRequired) == 0); BOOL isNetworkReachable = (isReachable && (!needsConnection || canConnectWithoutUserInteraction)); AFNetworkReachabilityStatus status = AFNetworkReachabilityStatusUnknown; if (isNetworkReachable == NO) { status = AFNetworkReachabilityStatusNotReachable; }#ifTARGET_OS_IPHONE else if ((flags & kSCNetworkReachabilityFlagsIsWWAN) != 0) { status = AFNetworkReachabilityStatusReachableViaWWAN; }#endif else { status = AFNetworkReachabilityStatusReachableViaWiFi; } return status;}
然後非同步執行callback回調方法,自然的我們自訂的block屬性也被調用了,上邊解釋過了
最後發送AFNetworkingReachabilityDidChangeNotification通知,這個通知攜帶了網路狀態情況
SCNetworkReachabilityScheduleWithRunLoop(self.networkReachability, CFRunLoopGetMain(), kCFRunLoopCommonModes);
這句代碼就是將網路連接引用狀態的變化監聽放入MainRunLoop中,為了在應用整個生命週期中,都能夠得到檢測和響應。
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0),^{ SCNetworkReachabilityFlags flags; if (SCNetworkReachabilityGetFlags(self.networkReachability, &flags)) { AFPostReachabilityStatusChange(flags, callback); } });
Boolean SCNetworkReachabilityGetFlags ( SCNetworkReachabilityRef target, SCNetworkReachabilityFlags *flags ); 這個函數用來獲得測試連接的狀態,第一個參數為之前建立的測試連接的引用,第二個參數用來儲存獲得的狀態,如果能獲得狀態則返回TRUE,否則返回FALSE
先非同步執行一個網路連接引用狀態是否改變的監聽
- (void)stopMonitoring { if (!self.networkReachability) { return; } SCNetworkReachabilityUnscheduleFromRunLoop(self.networkReachability, CFRunLoopGetMain(), kCFRunLoopCommonModes);}
停止網路連接引用狀態的監聽,從MainRunLoop中刪除掉
- (void)setReachabilityStatusChangeBlock:(void (^)(AFNetworkReachabilityStatus status))block { self.networkReachabilityStatusBlock = block;}
自訂我們的block內容,給self.networkReachabilityStatusBlock賦予我們想要的操作,以至於滿足項目需求
關於AFNetworkReachabilityManager的分析就講到這裡,以後遇到實際應用,再回來補充實際使用方式......