原文:http://itangqi.me/2016/05/17/the-notes-of-learning-afnetworking-six/
前言
AFNetworkReachabilityManager
是对 SystemConfiguration
模块的封装,苹果的文档中也有一个类似的项目 Reachability 这里对网络状态的监控跟苹果官方的实现几乎是完全相同的。
同样在 GitHub 上有一个类似的项目叫做 Reachability 不过这个项目由于命名的原因可能会在审核时被拒绝。
无论是 AFNetworkReachabilityManager
,苹果官方的项目或者说 GitHub 上的 Reachability,它们的实现都是类似的,而在这里我们会以 AFNetworking
中的 AFNetworkReachabilityManager
为例来说明在 iOS 开发中,我们是怎样监控网络状态的。
AFNetworkReachabilityManager 的使用和实现
AFNetworkReachabilityManager
的使用还是非常简单的,只需要三个步骤,就基本可以完成对网络状态的监控:
- 初始化
AFNetworkReachabilityManager
- 调用
startMonitoring
方法开始对网络状态进行监控 - 设置
networkReachabilityStatusBlock
在每次网络状态改变时, 调用这个 block
初始化 AFNetworkReachabilityManager
在初始化方法中,使用 SCNetworkReachabilityCreateWithAddress
或者 SCNetworkReachabilityCreateWithName
生成一个 SCNetworkReachabilityRef
的引用:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | + (instancetype)managerForDomain:(NSString *)domain { SCNetworkReachabilityRef reachability = SCNetworkReachabilityCreateWithName(kCFAllocatorDefault, [domain UTF8String]); AFNetworkReachabilityManager *manager = [[self alloc] initWithReachability:reachability]; return manager; } + (instancetype)managerForAddress:(const void *)address { SCNetworkReachabilityRef reachability = SCNetworkReachabilityCreateWithAddress(kCFAllocatorDefault, (const struct sockaddr *)address); AFNetworkReachabilityManager *manager = [[self alloc] initWithReachability:reachability]; return manager; } |
- 这两个方法会通过一个域名或者一个
sockaddr_in
的指针生成一个SCNetworkReachabilityRef
- 调用
- [AFNetworkReachabilityManager initWithReachability:]
将生成的SCNetworkReachabilityRef
引用传给networkReachability
- 设置一个默认的
networkReachabilityStatus
1 2 3 4 5 6 7 8 9 10 11 | - (instancetype)initWithReachability:(SCNetworkReachabilityRef)reachability { self = [super init]; if (!self) { return nil; } self.networkReachability = CFBridgingRelease(reachability); self.networkReachabilityStatus = AFNetworkReachabilityStatusUnknown; return self; } |
当调用
CFBridgingRelease(reachability)
后,会把reachability
桥接成一个 NSObject 对象赋值给self.networkReachability
,然后释放原来的 CoreFoundation 对象。
监控网络状态
在初始化 AFNetworkReachabilityManager
后,会调用 startMonitoring
方法开始监控网络状态:
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 | - (void)startMonitoring { // 1. 先调用 `- stopMonitoring` 方法,如果之前设置过对网络状态的监听,使用 `SCNetworkReachabilityUnscheduleFromRunLoop` 方法取消之前在 Main Runloop 中的监听 [self stopMonitoring]; if (!self.networkReachability) { return; } __weak __typeof(self)weakSelf = self; // 2. 创建一个在每次网络状态改变时的回调 AFNetworkReachabilityStatusBlock callback = ^(AFNetworkReachabilityStatus status) { __strong __typeof(weakSelf)strongSelf = weakSelf; strongSelf.networkReachabilityStatus = status; if (strongSelf.networkReachabilityStatusBlock) { strongSelf.networkReachabilityStatusBlock(status); } }; id networkReachability = self.networkReachability; // 3. 创建一个 `SCNetworkReachabilityContext`,其中的 `callback` 就是上一步中的创建的 block 对象 SCNetworkReachabilityContext context = {0, (__bridge void *)callback, AFNetworkReachabilityRetainCallback, AFNetworkReachabilityReleaseCallback, NULL}; // 4. 当目标的网络状态改变时,会调用传入的回调 SCNetworkReachabilitySetCallback((__bridge SCNetworkReachabilityRef)networkReachability, AFNetworkReachabilityCallback, &context); // 5. 在 Main Runloop 中对应的模式开始监控网络状态 SCNetworkReachabilityScheduleWithRunLoop((__bridge SCNetworkReachabilityRef)networkReachability, CFRunLoopGetMain(), kCFRunLoopCommonModes); // 6. 获取当前的网络状态,调用 callback dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0),^{ SCNetworkReachabilityFlags flags; if (SCNetworkReachabilityGetFlags((__bridge SCNetworkReachabilityRef)networkReachability, &flags)) { AFPostReachabilityStatusChange(flags, callback); } }); } |
1. 关于 AFNetworkReachabilityStatus
:
1 2 3 4 5 6 | typedef NS_ENUM(NSInteger, AFNetworkReachabilityStatus) { AFNetworkReachabilityStatusUnknown = -1, AFNetworkReachabilityStatusNotReachable = 0, AFNetworkReachabilityStatusReachableViaWWAN = 1, AFNetworkReachabilityStatusReachableViaWiFi = 2, }; |
2. 关于 SCNetworkReachabilityContext
:
1 2 3 4 5 6 7 8 9 10 11 12 | typedef struct { // 创建一个 SCNetworkReachabilityContext 结构体时,需要调用 SCDynamicStore 的创建函数,而此创建函数会根据 version 来创建出不同的结构体,SCNetworkReachabilityContext 对应的 version 是 0 CFIndex version; // 下面两个block(release 和 retain)的参数就是 info,此处表示的是网络状态处理的回调函数 void * __nullable info; // 该 retain block 用于对 info 进行 retain,下面那个 AFNetworkReachabilityRetainCallback 核心就是调用了 Block_copy(用于 retain 一个 block 函数,即在堆空间新建或直接引用一个 block 拷贝) const void * __nonnull (* __nullable retain)(const void *info); // 该 release block 用于对 info 进行 release,下面那个 AFNetworkReachabilityReleaseCallback 核心就是调用了 Block_release(用于 release 一个 block 函数,即将 block 从堆空间移除或移除相应引用) void (* __nullable release)(const void *info); // 提供 info 的 description,此处调用为 NULL CFStringRef __nonnull (* __nullable copyDescription)(const void *info); } SCNetworkReachabilityContext; |
在下一节中会介绍上面所提到的一些 C 函数以及各种回调。
设置 networkReachabilityStatusBlock 以及回调
在 Main Runloop 中对网络状态进行监控之后,在每次网络状态改变,就会调用 AFNetworkReachabilityCallback
函数:
1 2 3 | static void AFNetworkReachabilityCallback(SCNetworkReachabilityRef __unused target, SCNetworkReachabilityFlags flags, void *info) { AFPostReachabilityStatusChange(flags, (__bridge AFNetworkReachabilityStatusBlock)info); } |
这里会从 info
中取出之前存在 context
中的 AFNetworkReachabilityStatusBlock
。
1 2 3 4 5 6 7 8 9 10 | __weak __typeof(self)weakSelf = self; AFNetworkReachabilityStatusBlock callback = ^(AFNetworkReachabilityStatus status) { __strong __typeof(weakSelf)strongSelf = weakSelf; strongSelf.networkReachabilityStatus = status; if (strongSelf.networkReachabilityStatusBlock) { strongSelf.networkReachabilityStatusBlock(status); } }; |
取出这个 block 之后,传入 AFPostReachabilityStatusChange
函数:
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
获取当前的网络可达性状态 - 在主线程中异步执行上面传入的
callback
block(设置self
的网络状态,调用networkReachabilityStatusBlock
) - 发送
AFNetworkingReachabilityDidChangeNotification
通知.
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 | static AFNetworkReachabilityStatus AFNetworkReachabilityStatusForFlags(SCNetworkReachabilityFlags flags) { // 该网络地址可达 BOOL isReachable = ((flags & kSCNetworkReachabilityFlagsReachable) != 0); // 该网络地址虽然可达,但是需要先建立一个 connection BOOL needsConnection = ((flags & kSCNetworkReachabilityFlagsConnectionRequired) != 0); // 该网络虽然也需要先建立一个 connection,但是它是可以自动去 connect 的 BOOL canConnectionAutomatically = (((flags & kSCNetworkReachabilityFlagsConnectionOnDemand ) != 0) || ((flags & kSCNetworkReachabilityFlagsConnectionOnTraffic) != 0)); // 不需要用户交互,就可以 connect 上(用户交互一般指的是提供网络的账户和密码) BOOL canConnectWithoutUserInteraction = (canConnectionAutomatically && (flags & kSCNetworkReachabilityFlagsInterventionRequired) == 0); // 如果 isReachable==YES,那么就需要判断是不是得先建立一个 connection,如果需要,那就认为不可达,或者虽然需要先建立一个 connection,但是不需要用户交互,那么认为也是可达的 BOOL isNetworkReachable = (isReachable && (!needsConnection || canConnectWithoutUserInteraction)); // AFNetworkReachabilityStatus 就四种状态 Unknown、NotReachable、ReachableViaWWAN、ReachableViaWiFi,这四种状态字面意思很好理解,这里就不赘述了 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
,它的不同位代表了不同的网络可达性状态,通过 flags
的位操作,获取当前的状态信息 AFNetworkReachabilityStatus
。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | typedef CF_OPTIONS(uint32_t, SCNetworkReachabilityFlags) { kSCNetworkReachabilityFlagsTransientConnection = 1<<0, kSCNetworkReachabilityFlagsReachable = 1<<1, kSCNetworkReachabilityFlagsConnectionRequired = 1<<2, kSCNetworkReachabilityFlagsConnectionOnTraffic = 1<<3, kSCNetworkReachabilityFlagsInterventionRequired = 1<<4, kSCNetworkReachabilityFlagsConnectionOnDemand = 1<<5, // __OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_3_0) kSCNetworkReachabilityFlagsIsLocalAddress = 1<<16, kSCNetworkReachabilityFlagsIsDirect = 1<<17, #if TARGET_OS_IPHONE kSCNetworkReachabilityFlagsIsWWAN = 1<<18, #endif // TARGET_OS_IPHONE kSCNetworkReachabilityFlagsConnectionAutomatic = kSCNetworkReachabilityFlagsConnectionOnTraffic }; |
这里就是在 SystemConfiguration
中定义的全部的网络状态的标志位。
与 AFNetworking 协作
其实 AFNetworkReachabilityManager
与 AFNetworking
整个框架并没有太多的耦合。正相反,它在整个框架中作为一个即插即用的类使用,每一个 AFURLSessionManager
都会持有一个 AFNetworkReachabilityManager
的实例。
1
| self.reachabilityManager = [AFNetworkReachabilityManager sharedManager];
|
这是整个框架中除了 AFNetworkReachabilityManager.h/m
文件,唯一一个引用到这个类的地方。
总结
AFNetworkReachabilityManager
实际上只是对底层SystemConfiguration
库中的 C 函数封装的类(类似于 GCD),它为我们隐藏了 C 语言的实现,提供了统一且简洁的 Objective-C 语言接口- 它是
AFNetworking
中一个即插即用的模块