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 监控网络状态(四)