Reachability 是苹果官方提供的示例源码,它是对 SystemConfiguration.framework
模块中的 SCNetworkReachability.h
头文件里提供的一系列网络连接状态相关的 C 函数进行简单封装,以示范如何在 iOS App 开发中实现网络状态变化监听,由此也衍生出各种 Reachability
框架,比较著名的有 Github 上的 tonymillion/Reachability 以及 AFNetworking
中的 AFNetworkReachabilityManager
模块,它们的实现原理基本上是完全相同的。
下面我们就来阅读分析一下苹果提供的 Reachability 源码,源码中最核心的就 Reachability.h
和 Reachability.m
两个文件。
初始化方法
Reachability
中提供了三个快速初始化方法,分别为 reachabilityWithHostName:
、reachabilityWithAddress:
和 reachabilityForInternetConnection
。
reachabilityWithHostName: 方法
该方法通过指定的 服务器域名
初始化一个 Reachability
对象以进行判断网络连接状态,源码如下:
1 | + (instancetype)reachabilityWithHostName:(NSString *)hostName |
分析:上述代码比较简单,通过调用 SCNetworkReachabilityCreateWithName
C 函数生成一个 SCNetworkReachabilityRef
引用,然后初始化一个 Reachability
对象,并把刚才生成的引用赋给该对象中的 _reachabilityRef
成员变量,以供后面网络状态监听使用。
reachabilityWithAddress: 方法
该方法通过指定的 服务器 IP 地址
初始化一个 Reachability
对象以进行判断网络连接状态,源码如下:
1 | + (instancetype)reachabilityWithAddress:(const struct sockaddr *)hostAddress |
分析:与上述类似,该方法通过调用 SCNetworkReachabilityCreateWithAddress
C 函数生成一个 SCNetworkReachabilityRef
引用,并赋给 Reachability
对象中的 _reachabilityRef
成员变量。
reachabilityForInternetConnection 方法
该方法通过 默认的路由地址
初始化一个 Reachability
对象以进行判断网络连接状态,通常用于 App 没有连接到特定主机的情况,源码如下:
1 | + (instancetype)reachabilityForInternetConnection |
分析:在该方法中先初始化一个默认的 sockaddr_in
Socket 地址(这里创建的为零地址,0.0.0.0 地址表示查询本机的网络连接状态),然后调用 reachabilityWithAddress:
方法返回一个 Reachability
对象。
网络状态监听
开始监听
通过上述初始化方法获得一个 Reachability
对象后,可调用 startNotifier
方法开始进行网络状态变化的监听,源码如下:
1 | - (BOOL)startNotifier |
关于 SCNetworkReachabilityContext
的定义和注释如下:
1 | typedef struct { |
此处 Reachability
示例代码中创建的 context 的 info
取的是对象本身 self
(Reachability 对象类型),不是 block 类型,所以后面 retain
和 release
两个参数都取 NULL
,关于 SCNetworkReachabilityContext
的详细用法可参见 AFNetworkReachabilityManager.m
另外,上述回调函数 ReachabilityCallback
的定义如下,在该回调函数中,首先获取一个 Reachability
对象,并把该对象作为参数发送一个全局通知,因此我们可以监听 kReachabilityChangedNotification
通知以获得实时网络连接状态的变化。
1 | static void ReachabilityCallback(SCNetworkReachabilityRef target, SCNetworkReachabilityFlags flags, void* info) |
SCNetworkReachabilityCallBack
规定了自定义的回调函数的参数需要满足如下形式:
1 | typedef void (*SCNetworkReachabilityCallBack) ( |
取消监听
我们可调用 Reachability
对象的 stopNotifier
进行取消网络连接状态变化的监听,源码如下:
1 | - (void)stopNotifier |
释放对象
当要释放一个 Reachability 对象时,我们需要在其 dealloc
方法里取消网络状态监听。另外由于 SCNetworkReachabilityRef
是 Core Foundation
对象,所以这里需要调用 CFRelease()
函数释放 _reachabilityRef。
1 | - (void)dealloc |
获取当前网络连接状态
当通过上述方法初始化一个 Reachability
对象并调用 startNotifier
方法开始监听后,我们可以随时调用对象的 currentReachabilityStatus
方法获取当前网络连接状态,返回的状态类型 NetworkStatus
定义如下:
1 | typedef enum : NSInteger { |
currentReachabilityStatus
方法的实现源码如下,首先通过调用 SCNetworkReachabilityGetFlags(...)
函数并传入 _reachabilityRef
引用作为参数,获得一个表示当前网络连接状态的 SCNetworkReachabilityFlags
枚举值,然后根据枚举值调用 networkStatusForFlags:
方法判断当前网络状态类型并返回。
1 | - (NetworkStatus)currentReachabilityStatus |
networkStatusForFlags:
方法根据具体的 SCNetworkReachabilityFlags
枚举值,判断当前是否有网络连接,并且连接类型是 WiFi 还是 WWAN,具体实现和注释如下:
1 | - (NetworkStatus)networkStatusForFlags:(SCNetworkReachabilityFlags)flags |
在上述 networkStatusForFlags:
方法中,先调用了 PrintReachabilityFlags
函数打印当前网络连接状态对应的 flags
字符,根据拼接的不同字符我们可以判断不同的网络连接类型,比如 WiFi、2G、3G 等,该函数的实现如下:
1 |
|
比如,当是 WiFi 连接时会打印 “R”(这里忽略 “-“ 字符),当是 3G 连接时,打印 “Rt”,当是联通或移动 2G 连接时,则打印 “Rtc” 等等。
另外,在 Reachability
类中,还提供了一个 connectionRequired
方法,用于判断网络是否需要进一步连接(例如,虽然设备的 WWAN 连接可用,但并没有激活,需要建立一个连接来激活;或者虽然已连接上 WiFi,但该 WiFi 需要进一步 VPN 连接等情况),该方法通过验证 SCNetworkReachabilityFlags
值是否为 kSCNetworkReachabilityFlagsConnectionRequired
判断,实现如下:
1 | - (BOOL)connectionRequired |
使用示例
在 Reachability
源码的 APLViewController.m
文件中,苹果给出了上述封装的使用示例。在我们的 App 开发中,我们可以按如下步骤获取当前网络连接类型或者监听网络连接变化:
1 | // 1、添加 kReachabilityChangedNotification 通知监听,以监听网络连接变化; |
总结
通过分析上述 Reachability
源码,我们可以总结 SCNetworkReachability.h
头文件里提供的一系列网络连接状态相关的 C 函数的使用流程如下:
- 首先在
SCNetworkReachabilityCreateWithName(...)
、SCNetworkReachabilityCreateWithAddress(...)
、SCNetworkReachabilityCreateWithAddressPair(...)
3个初始化函数中任选其一创建一个SCNetworkReachabilityRef
引用; - 其次根据
SCNetworkReachabilityCallBack
定义一个网络监听回调函数,并初始化一个SCNetworkReachabilityContext
上下文信息,然后调用SCNetworkReachabilitySetCallback
函数并传入上述 ref、callback、context 3个参数,设置上述创建的 ref 在网络状态发生变化时的回调函数; - 通过调用
SCNetworkReachabilityScheduleWithRunLoop(...)
或SCNetworkReachabilityUnscheduleFromRunLoop(...)
函数并传入上述 ref,在 Current Runloop 中开始或取消监听网络连接状态变化,另外也可以通过SCNetworkReachabilitySetDispatchQueue(...)
函数设置在指定线程里监听; - 调用
SCNetworkReachabilityGetFlags(...)
函数并传入上述 ref,可获得当前网络连接状态的 flags 枚举值,另外需要注意的是,当 DNS 服务器无法连接,或者在弱网环境下,此函数将会很耗时,所以苹果建议在子线程里异步调用此函数; - 根据不同的
SCNetworkReachabilityFlags
枚举值,判断当前网络连接状态和连接类型。