AFNetworking 全解析之RechabilityManager
一个关于AFNetworking的系列
AFNetworking框架是一个使用非常广泛的框架,现在几乎已经占据了iOS开发中网络层的主导地位,成为行业标准。究其原因,其代码本身的质量非常高,设计非常优秀,使用也非常方便。
我们平时在网上看到的大多是如何来使用AFNetworking的文章,少有剖析实现原理。虽然我们没有必要再造一个轮子,但是理解轮子如何制造有助于提高我们自身的能力,也可以避免在使用过程中踩坑。
这个系列的文章是鄙人学习AF框架过程中所作的笔记,分享出来以供大家观摩批评,如有理解不到位的地方,欢迎大家批评指正。
AFNetworkReachabilityManager的作用
AFNetworkReachabilityManager是af库中用于临测网络状态,网络状态变化的工具。使用这个工具可以得到当前系统是否可接入网络,使用什么方式接入网络(Wifi or WWAN?),以及在网络发生变化时得到相应的通知。
定义的几种网络状态
manager对象的networkReachabilityStatus 属性标记当前的网络状态,有以下几种选项
AFNetworkReachabilityStatusUnknown 未知的网络状态
AFNetworkReachabilityStatusNotReachable 当前处于一个无网的状态,或者被禁用的网络状态
AFNetworkReachabilityStatusReachableViaWWAN 当前网络使用移动
AFNetworkReachabilityStatusReachableViaWiFi 当前使用wifi连接网络
使用AF查看当前网络状态
可以使用提供的单例,也可以自己创建一个新的实例来检查网络
[[AFNetworkReachabilityManager sharedManager] startMonitoring]; [[AFNetworkReachabilityManager sharedManager] setReachabilityStatusChangeBlock:^(AFNetworkReachabilityStatus status) { NSString *statusstr = AFStringFromNetworkReachabilityStatus(status); NSLog(@"network : %@", statusstr); }];
注意,如果启动时就去获取,这时获得的状态可能会为Unknown
[[AFNetworkReachabilityManager sharedManager] startMonitoring]; NSString *statusstr = AFStringFromNetworkReachabilityStatus(status); // statusstr = @"unknown...";
RechabilityManager类中所涉及的知识点
这里列举中间所用到的不常见的知识点,诸如单例、静态函数等不在此列
SCNetworkReachabilityRef
SCNetworkReachabilityRef 是SystemConfiguration framework中的一个类,专门用于检测和关注网络状态,使用C语言风格进行编程。
主要的函数有以下几个:SCNetworkRechabilityRef SCNetworkReachabilityCreateWithAddress(CFAllocatorRef allocator, const struct sockaddr *address)
创建该对象的引用,第一个参数可以使用NULL或者kCFAllocatorDefault,第二个参数为目标地址,如果传入0.0.0.0则用于查询本机网络状态。
在我们的AFReachabilityManager中,+manager方法使用了这个函数来创建一个默认的,用于0.0.0.0的连接器。
struct sockaddr_in address; bzero(&address, sizeof(address)); address.sin_len = sizeof(address); address.sin_family = AF_INET; [self managerForAddress:&address];
Boolean SCNetworkReachabilityGetFlags(SCNetworkReachabilityRef target, SCNetworkReachabilityFlags *flags)
用于获取网络的连接状态值,如果获取成功,返回TRUE。
Boolean SCNetworkReachabilityScheduleWithRunLoop ( SCNetworkReachabilityRef target, CFRunLoopRef runLoop, CFStringRef runLoopMode ) Boolean SCNetworkReachabilityUnscheduleFromRunLoop ( SCNetworkReachabilityRef target, CFRunLoopRef runLoop, CFStringRef runLoopMode ) Boolean SCNetworkReachabilitySetCallback ( SCNetworkReachabilityRef target, SCNetworkReachabilityCallBack __nullable callout, SCNetworkReachabilityContext * __nullable context )
这三个函数用于将screachability对象加入到runloop中,在网络状态发生变化时触发回调获得相应的通知。
要使用这三个方法,首先必须先配置好回调函数
static void AFNetworkReachabilityCallback(SCNetworkReachabilityRef __unused target, SCNetworkReachabilityFlags flags, void *info) { AFPostReachabilityStatusChange(flags, (__bridge AFNetworkReachabilityStatusBlock)info); }
然后使用 SCNetworkReachabilitySetCallback(self.networkReachability, AFNetworkReachabilityCallback, &context);
函数将callback添加到screachability中。
注意这里还有一个context参数 SCNetworkReachabilityContext context = {0, (__bridge void *)callback, AFNetworkReachabilityRetainCallback, AFNetworkReachabilityReleaseCallback, NULL};
callback是一个已定义好的block对象,而retainCallback和releaseCallback则决定了之后如何对这个callback对象进行引用。
这个callback将在随后的回调触发中,被传入到AFNetworkReachabilityCallback函数中被使用。
KeyValueAffecting
KVO机制我们在平时的开发中也经常会有使用,不过比较深入和偏门一些的知识点则不是那么的常见了,在AFReachabilityManager中使用到了一个方法
+(NSSet )keyPathsForValuesAffectingValueForKey:(NSString )key,这个方法决定了哪些key值会影响到其他的key值的observing。
比如
+(NSSet *)keyPathsForValuesAffectingValueForKey:(NSString *)key { if ([key isEqualToString:@"reachable"] || [key isEqualToString:@"reachableViaWWAN"] || [key isEqualToString:@"reachableViaWiFi"]) { return [NSSet setWithObject:@"networkReachabilityStatus"]; } return [super keyPathsForValuesAffectingValueForKey:key]; }
这个方法指明,如果networkReachabilityStatus属性被改变,那么reachable、reachableViaWWAN、reachableViaWiFi这几个属性的观察者也会收到通知。
UML结构图
调用流程图
这个流程图标识了AFReachability使用SCNetworkReachability并产生回调的过程
AFReachability的缺点
通过上面的流程图,大家可以看到,AFReachability对象创建以后并不会立即去取status,而是在startMonitor方法以后,才会去取得SC中的status,
即使如此,源代码中也是使用异步的方法去取的SCReachability的status,所以我们在使用startMonitor方法之后,并不能稳定的取得当前的网络状态,而是必需在回调中才能得到。
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0),^{ SCNetworkReachabilityFlags flags; if (SCNetworkReachabilityGetFlags(self.networkReachability, &flags)) { AFPostReachabilityStatusChange(flags, callback); } });