AFNetworkReachabilityManager 阅读笔记
AFNetworkReachabilityManager 是 AFNetworking 中用来监听网络可达性的组件,(有没有网络,什么网络)之类的。
ps:不能根据这个状态来阻止用户发送网络请求。
我们项目中,对网络状态变化做了一层封装,如下:
@interface PTVNetworkStatus()
@property(strong,nonatomic)AFHTTPSessionManager* session;
@end
@implementation PTVNetworkStatus
-(id)init
{
if (self = [super init]) {
_session = [[AFHTTPSessionManager alloc] initWithBaseURL:[NSURL URLWithString:@"https://api.m.panda.tv"]];
_session.securityPolicy = [AFSecurityPolicy policyWithPinningMode:AFSSLPinningModeNone];
[_session.reachabilityManager setReachabilityStatusChangeBlock:^(AFNetworkReachabilityStatus status) {
switch (status) {
case AFNetworkReachabilityStatusReachableViaWWAN:
NSLog(@"-------AFNetworkReachabilityStatusReachableViaWWAN------");
break;
case AFNetworkReachabilityStatusReachableViaWiFi:
NSLog(@"-------AFNetworkReachabilityStatusReachableViaWiFi------");
break;
case AFNetworkReachabilityStatusNotReachable:
NSLog(@"-------AFNetworkReachabilityStatusNotReachable------");
break;
default:
break;
}
if (_status != status) {
_status = status;
[[NSNotificationCenter defaultCenter] postNotificationName:notificationName object:nil];
}
}];
[_session.reachabilityManager startMonitoring];
}
return self;
}
我觉得这个包装有问题。所以决定深入看一下代码,
_session = [[AFHTTPSessionManager alloc] initWithBaseURL:[NSURL URLWithString:@"https://xxxxx.xxxxx"]];
_session.securityPolicy = [AFSecurityPolicy policyWithPinningMode:AFSSLPinningModeNone];
这里initWithBaseUrl 的参数 https://xxxxx.xxxxx 对网络状态监听并没有影响。
纠错
我们看看 AFHTTPSessionManager 的初始化函数:
在 AFURLSessionManager 的 init 函数中有默认的 reachabilityManager 实现。
self.reachabilityManager = [AFNetworkReachabilityManager sharedManager];
默认的实现是不会用到刚才传入的参数的。
+ (instancetype)sharedManager {
static AFNetworkReachabilityManager *_sharedManager = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
struct sockaddr_in address;
bzero(&address, sizeof(address));
address.sin_len = sizeof(address);
address.sin_family = AF_INET;
_sharedManager = [self managerForAddress:&address];
});
return _sharedManager;
}
所以项目中的正确用法应该是使用 managerForDomain 函数进行初始化 AFNetworkReachabilityManager 对象。然后使用 setReachabilityStatusChangeBlock 监听网络状态。。。
比如:
AFNetworkReachabilityManager *manager = [AFNetworkReachabilityManager managerForDomain:@"www.google.com"];
[manager setReachabilityStatusChangeBlock:^(AFNetworkReachabilityStatus status){
switch (status) {
case AFNetworkReachabilityStatusReachableViaWWAN:
case AFNetworkReachabilityStatusReachableViaWiFi:
case AFNetworkReachabilityStatusNotReachable:
NSLog(@"Never called");
break;
default:
NSLog(@"Never called");
break;
}
}];
[manager startMonitoring];
如果项目要包装 AFNetworkReachabilityManager 也不建议在 block 中直接发送通知,因为都在同一个类,如果通知忘记清空,倒是还有可能导致崩溃。
建议使用target/action 的方式,包装!一个列表,足以维护某个类需要的网络请求。
AFNetworkReachabilityManager 实现原理。
主要是基于 SCNetworkReachabilityRef 对网络状态进行监听的,系统本身已经支持监听网络状态,只是C 语言的形式,加上函数指针等等,对iOS 开发者,并不是很友好使用起来。
所以弄懂 AFNetworkReachabilityManager 只要点击看看 SCNetworkReachabilityRef 的相关函数以及文档就好。。
+ (instancetype)managerForDomain:(NSString *)domain {
SCNetworkReachabilityRef reachability = SCNetworkReachabilityCreateWithName(kCFAllocatorDefault, [domain UTF8String]);
AFNetworkReachabilityManager *manager = [[self alloc] initWithReachability:reachability];
manager.networkReachabilityAssociation = AFNetworkReachabilityForName;
return manager;
}
上面的函数式依据domain 创建一个 监听网络的对象。首先先创建一个 SCNetworkReachabilityRef 引用。
SCNetworkReachabilityRef的官方说明如下:
The SCNetworkReachability API allows an application to
determine the status of a system's current network
configuration and the reachability of a target host.
In addition, reachability can be monitored with notifications
that are sent when the status has changed.
重点在于 函数 startMonitoring
- (void)startMonitoring {
//先停止当前的监听
[self stopMonitoring];
///如果没有 SCNetworkReachabilityRef 不能监听
if (!self.networkReachability) {
return;
}
///创建网络变化回调的block
__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 结构,结构包含了用户指定的信息,和回调函数
SCNetworkReachabilityContext context = {0, (__bridge void *)callback, AFNetworkReachabilityRetainCallback, AFNetworkReachabilityReleaseCallback, NULL};
///设置网络变化的回调
SCNetworkReachabilitySetCallback((__bridge SCNetworkReachabilityRef)networkReachability, AFNetworkReachabilityCallback, &context);
///指定网络监听的runloop
SCNetworkReachabilityScheduleWithRunLoop((__bridge SCNetworkReachabilityRef)networkReachability, CFRunLoopGetMain(), kCFRunLoopCommonModes);
switch (self.networkReachabilityAssociation) {
case AFNetworkReachabilityForName:
break;
case AFNetworkReachabilityForAddress:
case AFNetworkReachabilityForAddressPair:
default: {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0),^{
SCNetworkReachabilityFlags flags;
SCNetworkReachabilityGetFlags((__bridge SCNetworkReachabilityRef)networkReachability, &flags);
AFNetworkReachabilityStatus status = AFNetworkReachabilityStatusForFlags(flags);
dispatch_async(dispatch_get_main_queue(), ^{
callback(status);
NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter];
[notificationCenter postNotificationName:AFNetworkingReachabilityDidChangeNotification object:nil userInfo:@{ AFNetworkingReachabilityNotificationStatusItem: @(status) }];
});
});
}
break;
}
}
开始监听函数的实现过程。
停止监听的过程如下:
- (void)stopMonitoring {
if (!self.networkReachability) {
return;
}
SCNetworkReachabilityUnscheduleFromRunLoop((__bridge SCNetworkReachabilityRef)self.networkReachability, CFRunLoopGetMain(), kCFRunLoopCommonModes);
}
直接从runloop中移除当前的网络监听对象、
小结
使用第三方功能的时候,一定要对源码有足够的了解才行。