目录
前言
AFNetworkReachabilityManager.h
状态枚举
四个属性
初始化方法
开始和关闭
返回网络状态的字符串
网络状态改变时的回调
AFNetworkReachabilityManager.m
初始化
开始监听
将系统 SCNetworkReachabilityFlags 转化成 AF 对外的网络状态枚举 AFNetworkReachabilityStatus
注册键值依赖
API 注释 Demo
参考资料
前言
AFNetworking 源码第二篇
主要看了看 AFNetworkReachabilityManager 的内容
作为一个辅助模块, 代码量和文件都比较少
一行一行读下来就可以了
AFN 概述:iOS 源码补完计划 --AFNetworking 3.1.0 源码研读
AFNetworkReachabilityManager.h
状态枚举
我们平时使用的枚举, 就是这四个.
- // 网络状态
- typedef NS_ENUM(NSInteger, AFNetworkReachabilityStatus) {
- AFNetworkReachabilityStatusUnknown = -1,// 未知
- AFNetworkReachabilityStatusNotReachable = 0,// 无网络
- AFNetworkReachabilityStatusReachableViaWWAN = 1,// 运营商网络
- AFNetworkReachabilityStatusReachableViaWiFi = 2,//WiFi 网络
- };
四个属性
也可以单独判断是否在某个环境下
均为只读
- /**
- 当前网络状态
- */
- @property (readonly, nonatomic, assign) AFNetworkReachabilityStatus networkReachabilityStatus;
- /**
- 当前是否有网络
- */
- @property (readonly, nonatomic, assign, getter = isReachable) BOOL reachable;
- /**
- 当前是否为运营商网络
- */
- @property (readonly, nonatomic, assign, getter = isReachableViaWWAN) BOOL reachableViaWWAN;
- /**
- 当前是否为 WiFi
- */
- @property (readonly, nonatomic, assign, getter = isReachableViaWiFi) BOOL reachableViaWiFi;
初始化方法
- /**
- 全局单例
- */
- + (instancetype)sharedManager;
- /**
- 工厂方法
- */
- + (instancetype)manager;
- /**
- 监控指定网址的管理器
- */
- + (instancetype)managerForDomain:(NSString *)domain;
- /**
- 监听某个 IP 的管理器
- */
- + (instancetype)managerForAddress:(const void *)address;
- /**
- 通过 SCNetworkReachabilityRef 对象初始化, 其余所有的方法也会汇聚于此
- */
- - (instancetype)initWithReachability:(SCNetworkReachabilityRef)reachability NS_DESIGNATED_INITIALIZER;
最后一个
initWithReachability:
是推荐的方法, 因为其他所有的方法最后内部都是通过它初始化
开始和关闭
- /**
- 打开监听
- */
- - (void)startMonitoring;
- /**
- 关闭监听
- */
- - (void)stopMonitoring;
返回网络状态的字符串
大概是方便开发者获取并且直接提示给用户吧
- (NSString *)localizedNetworkReachabilityStatusString;
网络状态改变时的回调
1, 通过 block:
- (void)setReachabilityStatusChangeBlock:(nullable void (^)(AFNetworkReachabilityStatus status))block;
2, 通过监听
- FOUNDATION_EXPORT NSString * const AFNetworkingReachabilityDidChangeNotification;
- FOUNDATION_EXPORT NSString * const AFNetworkingReachabilityNotificationStatusItem;
关于 FOUNDATION_EXPORT 和 UIKIT_EXTERN 的选择:
有人说是如果文件基于 FOUNDATION 则用前者, 反之则用后者.
二者都能替代 #define, 并且通过地址比对常量 (
也就是可以通过 == 直接进行比较
), 效率更高.
AFNetworkReachabilityManager.m
初始化
所有的初始化最后都归结于这里: 用
SCNetworkReachabilityRef
对象初始化
- - (instancetype)initWithReachability:(SCNetworkReachabilityRef)reachability {
- self = [super init];
- if (!self) {
- return nil;
- }
- _networkReachability = CFRetain(reachability);
- self.networkReachabilityStatus = AFNetworkReachabilityStatusUnknown;
- return self;
- }
基于它的两个工厂方法
- + (instancetype)managerForDomain:(NSString *)domain {
- /*
- SCNetworkReachabilityRef SCNetworkReachabilityCreateWithName ( // 根据传入的网址创建网络连接引用
- CFAllocatorRef allocator, // 可以为 NULL 或 kCFAllocatorDefault
- const char *nodename // 比如为 "www.baidu.com", 此参数为域名
- );
- 这个是根据传入的网址测试连接,
- 第一个参数 可以为 NULL 或 kCFAllocatorDefault,
- 第二个参数比如为 "www.apple.com", 其他和上一个一样.
- */
- SCNetworkReachabilityRef reachability = SCNetworkReachabilityCreateWithName(kCFAllocatorDefault, [domain UTF8String]);
- AFNetworkReachabilityManager *manager = [[self alloc] initWithReachability:reachability];
- CFRelease(reachability);
- return manager;
- }
- + (instancetype)managerForAddress:(const void *)address {
- /*
- SCNetworkReachabilityRef SCNetworkReachabilityCreateWithAddress ( // 根据传入的地址创建网络连接引用
- CFAllocatorRef allocator, // 可以为 NULL 或 kCFAllocatorDefault
- const struct sockaddr *address // 需要测试连接的 IP 地址
- );
- 第一个参数 可以为 NULL 或 kCFAllocatorDefault,
- 第二个参数 为需要测试连接的 IP 地址, 当为 0.0.0.0 时则可以查询本机的网络连接状态.
- */
- SCNetworkReachabilityRef reachability = SCNetworkReachabilityCreateWithAddress(kCFAllocatorDefault, (const struct sockaddr *)address);
- AFNetworkReachabilityManager *manager = [[self alloc] initWithReachability:reachability];
- CFRelease(reachability);
- return manager;
- }
需要注意的是初始化所用的
SCNetworkReachabilityRef
引用, 必须在使用后 CFRelease()
- + (instancetype)manager
- {
- #if (defined(__IPHONE_OS_VERSION_MIN_REQUIRED) && __IPHONE_OS_VERSION_MIN_REQUIRED>= 90000) || (defined(__MAC_OS_X_VERSION_MIN_REQUIRED) && __MAC_OS_X_VERSION_MIN_REQUIRED>= 101100)
- struct sockaddr_in6 address;
- bzero(&address, sizeof(address));
- address.sin6_len = sizeof(address);
- address.sin6_family = AF_INET6;
- #else
- struct sockaddr_in address;
- bzero(&address, sizeof(address));
- address.sin_len = sizeof(address);
- address.sin_family = AF_INET;
- #endif
- return [self managerForAddress:&address];
- }
这个似乎涉及到了 socket, 等我研究研究补上~
再有就是关于
#if - #esle - #endif
其实用普通的 if-else 也是一样, 好处就是在编译阶段是否会被编译.
不过,
#if - #esle - #endif
不能用来判断一个动态的语法.
开始监听
- - (void)startMonitoring {
- [self stopMonitoring];
- if (!self.networkReachability) {
- return;
- }
- __weak __typeof(self)weakSelf = self;
- // 网络状态改变的时候, 执行这个 block
- // 为什么这么写? 因为 AFPostReachabilityStatusChange 会返回我们需要的 status, 但必须要一个 block 接受
- // 并没什么特殊的含义, 单纯的方便接受 status 罢了
- AFNetworkReachabilityStatusBlock callback = ^(AFNetworkReachabilityStatus status) {
- __strong __typeof(weakSelf)strongSelf = weakSelf;
- // 改变 manager 的网络状态枚举
- strongSelf.networkReachabilityStatus = status;
- // 调用用户传入的 block
- if (strongSelf.networkReachabilityStatusBlock) {
- strongSelf.networkReachabilityStatusBlock(status);
- }
- };
- /*
- typedef struct {
- // 接受一个 signed long 的参数
- CFIndex version;
- // 接受一个 void * 类型的值, 相当于 oc 的 id 类型, void * 可以指向任何类型的参数
- void * __nullable info;
- // 接受一个函数 目的是对 info 做 retain 操作,
- const void * __nonnull (* __nullable retain)(const void *info);
- // 接受一个函数, 目的是对 info 做 release 操作
- void (* __nullable release)(const void *info);
- // 接受一个函数, 根据 info 获取 Description 字符串
- CFStringRef __nonnull (* __nullable copyDescription)(const void *info);
- } SCNetworkReachabilityContext;
- 对于这些参数, 都不是必须传一个 block 或者怎样. 如果 info 不是 block, 那么后面的 reatin 啥的都可以忽略
- SCNetworkReachabilityContext context = {0, (__bridge void *)(self), NULL, NULL, NULL};
- */
- // 新建上下文
- SCNetworkReachabilityContext context = {0, (__bridge void *)callback, AFNetworkReachabilityRetainCallback, AFNetworkReachabilityReleaseCallback, NULL};
- // 设置回调 -- 这里 AFNetworkReachabilityCallback 可以上面的 info, 也就是 callback 那个 block 进行处理
- SCNetworkReachabilitySetCallback(self.networkReachability, AFNetworkReachabilityCallback, &context);
- // 放入 RunLoop 池
- SCNetworkReachabilityScheduleWithRunLoop(self.networkReachability, CFRunLoopGetMain(), kCFRunLoopCommonModes);
- dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0),^{
- SCNetworkReachabilityFlags flags;
- if (SCNetworkReachabilityGetFlags(self.networkReachability, &flags)) {
- // 网络状态改变 -- 去获取当前网络状态. 然后发通知, 调用 block
- AFPostReachabilityStatusChange(flags, callback);
- }
- });
- }
我对
SCNetworkReachability
没啥研究, 只能一遍查资料一遍顺便注释一下, 至少保证上面的代码能看懂.
将系统
SCNetworkReachabilityFlags
转化成 AF 对外的网络状态枚举
- AFNetworkReachabilityStatus
- 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);
- // 是否可以自动联网 => 条件: 1. 存在网络 2. 可以自动尝试并完成连接
- 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 {
- //WiFi
- status = AFNetworkReachabilityStatusReachableViaWiFi;
- }
- return status;
- }
关于
SCNetworkReachabilityFlags
枚举
- typedef CF_OPTIONS(uint32_t, SCNetworkReachabilityFlags) {
- // 可以通过瞬时连接 (例如 PPP--- 点对点通讯协议) 链接到给定的节点名和地址
- kSCNetworkReachabilityFlagsTransientConnection = 1<<0,
- // 能够连接网络
- kSCNetworkReachabilityFlagsReachable = 1<<1,
- // 能够连接网络, 但是首先得建立连接过程
- kSCNetworkReachabilityFlagsConnectionRequired = 1<<2, = 1<<3,
- // 当前网络配置可以请求到给定的节点名和地址, 但必须先通过确认用户创建一个连接.
用户需要通过某些方式的介入来创建该连接, 比如提供密码, 认证口令等. 一般的, 只有在一个拨号通信的配置中, 在自动连接过程中发生某些错误 (比如没有拨号音, 没有回应, 无效的密码等), 这种情况下, PPP 通信将会终止连接直到用户介入.
- kSCNetworkReachabilityFlagsInterventionRequired = 1<<4,
- // 当前网络配置可以请求到给定的节点名和地址, 但必须先创建一个连接. 该请求一经 CFSocketStream 程序接口请求就创建, 其他的函数不会创建该连接
- 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
- };
注册键值依赖
KVO 的一个冷门方法
- + (NSSet *)keyPathsForValuesAffectingValueForKey:(NSString *)key {
- if ([key isEqualToString:@"reachable"] || [key isEqualToString:@"reachableViaWWAN"] || [key isEqualToString:@"reachableViaWiFi"]) {
- return [NSSet setWithObject:@"networkReachabilityStatus"];
- }
- return [super keyPathsForValuesAffectingValueForKey:key];
- }
当 return 的 值被改变的时候, 触发 key 的监听
也就是说当
networkReachabilityStatus
改变的时候, reachable/reachableViaWWAN/reachableViaWiFi 的 KVO 监听都将被触发
API 注释 Demo
把注释的源码放在了 github 上, 有兴趣可以自取.
GitHub https://github.com/kiritoSong/KTAFNetWorkingDemo
参考资料
AFNetworking 3.0 源码解读 (一)AFNetworkReachabilityManager http://www.cnblogs.com/machao/p/5681645.html
SCNetworkReachability 说明使用 http://blog.sina.com.cn/s/blog_65a8ab5d010110w5.html
提升自己逼格的编程之美之代码规范 http://www.uml.org.cn/codeNorms/201702142.asp
http://www.cnblogs.com/Jacue/p/5081706.html iOS 判断网络连接状态之重写 Reachability http://www.cnblogs.com/Jacue/p/5081706.html
IOS SCNetworkReachability 和 Reachability 监测网络连接状态 https://blog.csdn.net/s3590024/article/details/51279863
iOS 开发,#define 的使用 (系列一) http://www.360doc.com/content/15/0302/17/20918780_452023776.shtml
iOS 概念之 KVO(Key-Value Observing http://ningandjiao.iteye.com/blog/2009729
来源: http://www.jianshu.com/p/7abd7312383d