AFNetworking 3.0 源码阅读笔记(六)

前言

AFNetworkReachabilityManager 是对 SystemConfiguration 模块的封装,苹果的文档中也有一个类似的项目 Reachability 这里对网络状态的监控跟苹果官方的实现几乎是完全相同的。

同样在 GitHub 上有一个类似的项目叫做 Reachability 不过这个项目由于命名的原因可能会在审核时被拒绝

无论是 AFNetworkReachabilityManager,苹果官方的项目或者说 GitHub 上的 Reachability,它们的实现都是类似的,而在这里我们会以 AFNetworking 中的 AFNetworkReachabilityManager 为例来说明在 iOS 开发中,我们是怎样监控网络状态的。


AFNetworkReachabilityManager 的使用和实现

AFNetworkReachabilityManager 的使用还是非常简单的,只需要三个步骤,就基本可以完成对网络状态的监控:

  1. 初始化 AFNetworkReachabilityManager
  2. 调用 startMonitoring 方法开始对网络状态进行监控
  3. 设置 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;
}
  1. 这两个方法会通过一个域名或者一个 sockaddr_in 的指针生成一个 SCNetworkReachabilityRef
  2. 调用 - [AFNetworkReachabilityManager initWithReachability:] 将生成的 SCNetworkReachabilityRef 引用传给 networkReachability
  3. 设置一个默认的 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];
});
}
  1. 调用 AFNetworkReachabilityStatusForFlags 获取当前的网络可达性状态
  2. 在主线程中异步执行上面传入的 callback block(设置 self 的网络状态,调用 networkReachabilityStatusBlock
  3. 发送 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 协作

其实 AFNetworkReachabilityManagerAFNetworking 整个框架并没有太多的耦合。正相反,它在整个框架中作为一个即插即用的类使用,每一个 AFURLSessionManager 都会持有一个 AFNetworkReachabilityManager 的实例。

1
self.reachabilityManager = [AFNetworkReachabilityManager sharedManager];

这是整个框架中除了 AFNetworkReachabilityManager.h/m 文件,唯一一个引用到这个类的地方。


总结

  • AFNetworkReachabilityManager 实际上只是对底层 SystemConfiguration 库中的 C 函数封装的类(类似于 GCD),它为我们隐藏了 C 语言的实现,提供了统一且简洁的 Objective-C 语言接口
  • 它是 AFNetworking 中一个即插即用的模块

参考