主要参考:
AFNetworking 全解析之RechabilityManager
AFNetworkReachabilityManager 监控网络状态
AFNetworkReachabilityManager
是对systemConfiguration
模块的封装,跟苹果文档中Reachability
的实现基本一致,本节我们将以AFNetworking
中AFNetworkReachabilityManager
为例来说明在iOS
开发中,如何实现网络监控状态.
一. AFNetworkReachabilityManager 的使用和实现
AFNetworkReachabilityManager
对象的networkReachabilityStatus
属性标记当前的网络状态,有以下几种选项
AFNetworkReachabilityStatusUnknown 未知的网络状态
AFNetworkReachabilityStatusNotReachable 当前处于一个无网的状态,或者被禁用的网络状态
AFNetworkReachabilityStatusReachableViaWWAN 当前网络使用移动
AFNetworkReachabilityStatusReachableViaWiFi 当前使用wifi连接网络
AFNetworkReachabilityManager
的使用是非常简单的,只需要三个步骤,就基本可以完成对网络状态的监控。
- 初始化 AFNetworkReachabilityManager
- 调用 startMonitoring 方法开始对网络状态进行监控
- [设置 networkReachabilityStatusBlock 在每次网络状态改变时, 调用这个 block
初始化 AFNetworkReachabilityManager
在初始化方法中,使用 SCNetworkReachabilityCreateWithAddress
或者 SCNetworkReachabilityCreateWithName
生成一个 SCNetworkReachabilityRef
的引用。
+ (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
- (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
方法开始监控网络状态。
- (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);
}
};
id networkReachability = self.networkReachability;
SCNetworkReachabilityContext context = {0, (__bridge void *)callback, AFNetworkReachabilityRetainCallback, AFNetworkReachabilityReleaseCallback, NULL};
SCNetworkReachabilitySetCallback((__bridge SCNetworkReachabilityRef)networkReachability, AFNetworkReachabilityCallback, &context);
SCNetworkReachabilityScheduleWithRunLoop((__bridge SCNetworkReachabilityRef)networkReachability, CFRunLoopGetMain(), kCFRunLoopCommonModes);
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0),^{
SCNetworkReachabilityFlags flags;
if (SCNetworkReachabilityGetFlags((__bridge SCNetworkReachabilityRef)networkReachability, &flags)) {
AFPostReachabilityStatusChange(flags, callback);
}
});
}
- (void)stopMonitoring {
if (!self.networkReachability) {
return;
}
SCNetworkReachabilityUnscheduleFromRunLoop((__bridge SCNetworkReachabilityRef)self.networkReachability, CFRunLoopGetMain(), kCFRunLoopCommonModes);
}
-
先调用
- stopMonitoring
方法,如果之前设置过对网络状态的监听,使用SCNetworkReachabilityUnscheduleFromRunLoop
方法取消之前在Main Runloop
中的监听 -
创建一个在每次网络状态改变时的回调,每次回调被调用时,重新设置
networkReachabilityStatus
属性,调用networkReachabilityStatusBlock
。 -
创建一个
SCNetworkReachabilityContext
typedef struct {
CFIndex version;
void * __nullable info;
const void * __nonnull (* __nullable retain)(const void *info);
void (* __nullable release)(const void *info);
CFStringRef __nonnull (* __nullable copyDescription)(const void *info);
} SCNetworkReachabilityContext;
SCNetworkReachabilityContext context = {
0,
(__bridge void *)callback,
AFNetworkReachabilityRetainCallback,
AFNetworkReachabilityReleaseCallback,
NULL
};
-
其中的
callback
就是上一步中的创建的block
对象 -
这里的
AFNetworkReachabilityRetainCallback
和AFNetworkReachabilityReleaseCallback
都是非常简单的block
,在回调被调用时,只是使用Block_copy
和Block_release
这样的宏 -
传入的
info
会以参数的形式在AFNetworkReachabilityCallback
执行时传入static const void * AFNetworkReachabilityRetainCallback(const void *info) { return Block_copy(info); } static void AFNetworkReachabilityReleaseCallback(const void *info) { if (info) { Block_release(info); } }
-
当目标的网络状态改变时,会调用传入的回调
SCNetworkReachabilitySetCallback( (__bridge SCNetworkReachabilityRef)networkReachability, AFNetworkReachabilityCallback, &context );
-
在
Main Runloop
中对应的模式开始监控网络状态SCNetworkReachabilityScheduleWithRunLoop( (__bridge SCNetworkReachabilityRef)networkReachability, CFRunLoopGetMain(), kCFRunLoopCommonModes );
-
获取当前的网络状态,调用
callback
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0),^{ SCNetworkReachabilityFlags flags; if (SCNetworkReachabilityGetFlags((__bridge SCNetworkReachabilityRef)networkReachability, &flags)) { AFPostReachabilityStatusChange(flags, callback); } });
设置 networkReachabilityStatusBlock 以及回调
在 Main Runloop
中对网络状态进行监控之后,在每次网络状态改变,就会调用AFNetworkReachabilityCallback
函数:
static void AFNetworkReachabilityCallback(SCNetworkReachabilityRef __unused target, SCNetworkReachabilityFlags flags, void *info) {
AFPostReachabilityStatusChange(flags, (__bridge AFNetworkReachabilityStatusBlock)info);
}
这里会从info
中取出之前存在 context
中的 AFNetworkReachabilityStatusBlock
。
__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
函数:
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
通知.
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;
}
因为 flags
是一个 SCNetworkReachabilityFlags
,它的不同位代表了不同的网络可达性状态,通过 flags
的位操作,获取当前的状态信息 AFNetworkReachabilityStatus
。
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 中定义的全部的网络状态的标志位。
二. AFNetworkReachabilityManager 调用流程
UML结构图:

调用流程图:
这个流程图标识了AFReachability
使用SCNetworkReachability
并产生回调的过程

三. 与 AFNetworking 协作
其实这个类与 AFNetworking
整个框架并没有太多的耦合。正相反,它在整个框架中作为一个即插即用的类使用,每一个 AFURLSessionManager
都会持有一个 AFNetworkReachabilityManager
的实例。
self.reachabilityManager = [AFNetworkReachabilityManager sharedManager];
这是整个框架中除了 AFNetworkReachabilityManager.h/m
文件,唯一一个引用到这个类的地方。
在实际的使用中,我们也可以直接操作 AFURLSessionManager
的 reachabilityManager
来获取当前的网络可达性状态,而不是自己手动初始化一个实例,当然这么做也是没有任何问题的。
四.总结
-
AFNetworkReachabilityManager
实际上只是一个对底层SystemConfiguration
库中的C 函数
封装的类,它为我们隐藏了C 语言
的实现,提供了统一的Objective-C
语言接口 -
它是
AFNetworking
中一个即插即用的模块,具有很好的独立性。
网友评论