AFNetworkReachabilityManager是AFNetworking框架中通过监控蜂窝网络和wifi网络接口的域名和地址的可达性的网络状态管理类。它其实是封装了底层SystemConfiguration类,使得网络监控变得像一个插件一样,简单易用。
AFNetworkReachabilityManager分析 首先我们看一下头文件中的网络状态枚举。它里面有4种状态值,分为未知、网络不可达、蜂窝网络和Wifi。
1 2 3 4 5 6 typedef  NS_ENUM (NSInteger , AFNetworkReachabilityStatus) {    AFNetworkReachabilityStatusUnknown          = -1 ,     AFNetworkReachabilityStatusNotReachable     = 0 ,     AFNetworkReachabilityStatusReachableViaWWAN = 1 ,     AFNetworkReachabilityStatusReachableViaWiFi = 2 , }; 
 
然后我们看到一个宏组合。这表示在这两个宏之间的指针都是非空的。如果需要指定可以为空,则需单独指定nullable参数。
1 2 NS_ASSUME_NONNULL_BEGIN NS_ASSUME_NONNULL_END 
 
接着我们看到4个只读的属性值。其含义在代码中写的很清楚。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18  The current network reachability status.  */ @property  (readonly , nonatomic , assign ) AFNetworkReachabilityStatus networkReachabilityStatus;@property  (readonly , nonatomic , assign , getter  = isReachable) BOOL  reachable;@property  (readonly , nonatomic , assign , getter  = isReachableViaWWAN) BOOL  reachableViaWWAN;@property  (readonly , nonatomic , assign , getter  = isReachableViaWiFi) BOOL  reachableViaWiFi;
 
后面三个属性的判断实现也是非常简单的。通过比对networkReachabilityStatus的状态来判断。
1 2 3 4 5 6 7 8 9 10 11 - (BOOL )isReachable {     return  [self  isReachableViaWWAN] || [self  isReachableViaWiFi]; } - (BOOL )isReachableViaWWAN {     return  self .networkReachabilityStatus == AFNetworkReachabilityStatusReachableViaWWAN; } - (BOOL )isReachableViaWiFi {     return  self .networkReachabilityStatus == AFNetworkReachabilityStatusReachableViaWiFi; } 
 
接着我们看到+ (instancetype)sharedManager这个方法。这个方法一看就是一个单例方法。然后我们m文件中看实现。内部用了dispatch_once_t实现单例。
1 2 3 4 5 6 7 8 9 + (instancetype )sharedManager {     static  AFNetworkReachabilityManager *_sharedManager = nil ;     static  dispatch_once_t  onceToken;     dispatch_once (&onceToken, ^{         _sharedManager = [self  manager];     });     return  _sharedManager; } 
 
dispatch_once中调用了+ (instancetype)manager方法。这里区分了新老版本,新版本socket地址使用ipv6,老版本使用ipv4。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 + (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的最后调用了+ (instancetype)managerForAddress:(const void *)address。SCNetworkReachabilityRef是里面检测网络状态最核心的句柄,它通过刚才传入的地址初始化。另外SCNetworkReachabilityRef由于属于底层框架,不属于NSObject,需要手动管理引用计数。此外- (instancetype)initWithReachability:(SCNetworkReachabilityRef)reachability中设置了networkReachabilityStatus的初始化值为AFNetworkReachabilityStatusUnknown,同时把reachability赋给了networkReachability。这个networkReachability很关键,后面的逻辑判断都会用到它。到这里初始化就结束了。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 + (instancetype )managerForAddress:(const  void  *)address {     SCNetworkReachabilityRef  reachability = SCNetworkReachabilityCreateWithAddress (kCFAllocatorDefault, (const  struct  sockaddr *)address);     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 ; } 
 
另外,它还提供了从域名初始化,与从socket address初始化类似。只是没有提供相应的单例初始化方法。
1 2 3 4 5 6 7 8 9 + (instancetype )managerForDomain:(NSString  *)domain {     SCNetworkReachabilityRef  reachability = SCNetworkReachabilityCreateWithName (kCFAllocatorDefault, [domain UTF8String]);     AFNetworkReachabilityManager *manager = [[self  alloc] initWithReachability:reachability];          CFRelease (reachability);     return  manager; } 
 
看完了初始化,我们来到重头戏- (void)startMonitoring。在这个类的说明中指出,要检测网络状态,必须先执行这个方法。然后我们来看一下其内部逻辑。 1.执行了停止原先监控的操作。其内部就是退出runloop操作。 2.检查networkReachability是否存在,如果不存在,直接返回。 3.创建了一个callback的block回调,就是为了把网络状态通过networkReachabilityStatusBlock回调出去。这里面为了防止循环引用,引入了weak-strong dance。 4.创建了SCNetworkReachabilityContext,引入了上一步创建的callback。 5.设置了SCNetworkReachabilitySetCallback。当网络状态改变时会进入AFNetworkReachabilityCallback。 6.通过SCNetworkReachabilityScheduleWithRunLoop把这个检测任务丢入主runloop。 7.发起了一个异步优先级低的线程获取当前网络状态,并回调给callback。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 - (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);         }     }); } 
 
我们来看一下上面第5步用到的AFNetworkReachabilityCallback方法,这个一个静态方法,其中有三个参数target、flags和info。其中target和info就是SCNetworkReachabilitySetCallback中传入的self.networkReachability和&context。
1 2 3 static  void  AFNetworkReachabilityCallback(SCNetworkReachabilityRef  __unused target, SCNetworkReachabilityFlags  flags, void  *info) {    AFPostReachabilityStatusChange(flags, (__bridge AFNetworkReachabilityStatusBlock)info); } 
 
然后看到AFNetworkReachabilityCallback内部调用了另一个静态方法AFPostReachabilityStatusChange。这里通过AFNetworkReachabilityStatusForFlags方法来判断flags对应的网络状态。然后把状态通过block回调出去。同时还发了一个change的通知。
1 2 3 4 5 6 7 8 9 10 11 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];     }); } 
 
AFNetworkReachabilityStatusForFlags方法来判断状态其实很简单。SCNetworkReachabilityFlags是一个枚举值,通过综合判断flags的状态,就可以得出网络是否可达,是wifi网络还是蜂窝网络的判断。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 typedef  CF_OPTIONS (uint32_t, SCNetworkReachabilityFlags ) {	kSCNetworkReachabilityFlagsTransientConnection	= 1 <<0 , 	kSCNetworkReachabilityFlagsReachable		= 1 <<1 , 	kSCNetworkReachabilityFlagsConnectionRequired	= 1 <<2 , 	kSCNetworkReachabilityFlagsConnectionOnTraffic	= 1 <<3 , 	kSCNetworkReachabilityFlagsInterventionRequired	= 1 <<4 , 	kSCNetworkReachabilityFlagsConnectionOnDemand	= 1 <<5 ,	 	kSCNetworkReachabilityFlagsIsLocalAddress	= 1 <<16 , 	kSCNetworkReachabilityFlagsIsDirect		= 1 <<17 , #if 	TARGET_OS_IPHONE 	kSCNetworkReachabilityFlagsIsWWAN		= 1 <<18 , #endif 	 	kSCNetworkReachabilityFlagsConnectionAutomatic	= kSCNetworkReachabilityFlagsConnectionOnTraffic }; 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;     } #if 	TARGET_OS_IPHONE     else  if  ((flags & kSCNetworkReachabilityFlagsIsWWAN) != 0 ) {         status = AFNetworkReachabilityStatusReachableViaWWAN;     } #endif      else  {         status = AFNetworkReachabilityStatusReachableViaWiFi;     }     return  status; } 
 
这样整个- (void)startMonitoring就结束了。然后我们来看一下- (void)stopMonitoring停止监控的方法。 1.判断networkReachability是否为空,如果为空,则直接返回。 2.通过SCNetworkReachabilityUnscheduleFromRunLoop退出主runloop。
1 2 3 4 5 6 7 - (void)stopMonitoring {     if (!self.networkReachability) {         return;     }     SCNetworkReachabilityUnscheduleFromRunLoop(self.networkReachability, CFRunLoopGetMain(), kCFRunLoopCommonModes); } 
 
结语 至此,整个AFNetworkReachabilityManager类的分析就结束了。我们可以看到,它通过优雅的封装,使得网络状态的判断变得十分方便。另外私有函数都是通过静态函数的方法呈现。
参考 AFNetworking AFNetworkReachabilityManager 监控网络状态(四)