AFNetworking 源码阅读之网络监听 Reachability
AFNetWorking源码阅读
AFNetWorking 是一款用于 Cocoa 上的网络库,它适用于 iOS, macOS, watchOS, 以及 tvOS 等各个系统。AFNetWorking 的优点在于,它提供了一套非常全面并且易于使用的 API,让我们在隔绝和 Cocoa 原生网络架构的繁琐交互的过程中,编写与网络相关的代码。
阅读 AFNetWorking 源码不仅能让开发者更好的了解和运用这个人气超高的网络库,还能从中学到许多优秀的开发技巧,感受大神的风采。
AFNetWorking 源码中,主要包含了四大内容:
- Reachability 网络监听
- NSURLSession 的封装
- Security 安全策略
- Serialization 序列化
本文是对 Reachability 网络监听板块AFNetworkReachabilityManager 的源码阅读做的记录。
一、AFNetworkReachabilityManager 是如何使用的
AFNetworkReachabilityManager 是 AFNetWorking 的一个子板块,实际上它能够完全脱离 AFNetWorking 来使用。
以下是一段使用 AFNetWorking 的样例:
// 使用 block [[AFNetworkReachabilityManager sharedManager] setReachabilityStatusChangeBlock:^(AFNetworkReachabilityStatus status) { NSLog(@"block 获取当前网络状态:%d",status); }];// 使用通知中心 [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(networkChangeNotificationWithInfo:) name:AFNetworkingReachabilityDidChangeNotification object:nil];- (void)networkChangeNotificationWithInfo:(NSNotification)notification { NSDictionary *dic = notification.userInfo; if(dic) { AFNetworkReachabilityStatus status = dic[AFNetworkingReachabilityNotificationStatusItem]; NSLog(@"通知中心 获取当前网络状态:%d",status); }}以上分别是使用block和通知中心来监听网络变化,记得增加头文件
@import "AFNetworking.h"
或者者@import "AFNetworkReachabilityManager.h"
没有任何难度,这也许就是强大又全面的 API 的一种表征吧。
当然我们主要是要理解它的内部实现的。
二、 AFNetworkReachabilityManager 的实现
AFNetWorking 的网络监听是通过 AFNetworkReachabilityManager 类来实现的。
从 AFNetworkReachabilityManager 的 .h 文件暴露接口来看,这个类实际上相当的简单。文件中,除了定义了 AFNetworkReachabilityManager 类型之外,还包含一个表示网络状态的枚举和两个的网络通知的常量字段公告,以及一个网络监听的回调函数。
1. 网络状态枚举AFNetworkReachabilityStatus
源码中我增加了少量注释:
AFNetworkReachabilityStatus
这四个状态将表示整个网络监听功能的最终结果。
2. 两个网络通知的常量字段和一个网络状态形容的回调函数
image.png
2.1网络变化通知的常量字段
FOUNDATION_EXPORT NSString * const AFNetworkingReachabilityDidChangeNotification;FOUNDATION_EXPORT NSString * const AFNetworkingReachabilityNotificationStatusItem;这两个字段中, AFNetworkingReachabilityDidChangeNotification用于网络状态发生变化时的通知字段,我们在开发中,只需使用通知中心 NSNotificationCenter 监听这个字段,就能在每一次的网络改变时得到监听回调。这个通知中的回调参数是一个字典,字典中保存了网络状态,相似于:
{key:AFNetworkReachabilityStatus} 这个 key 是一个字符串,也即是上面的 AFNetworkingReachabilityNotificationStatusItem字段。我们得到字典之后,通过key值即可以获取到改变后的网络状态了。
2.2网络状态形容的回调函数
获取网络时,我们获取到的是AFNetworkReachabilityStatus的枚举值,有时候我们需要得到这个状态的形容,那么通过网络状态形容的回调函数就可获取:
FOUNDATION_EXPORT NSString * AFStringFromNetworkReachabilityStatus(AFNetworkReachabilityStatus status);它的内部实现这样的:
AFStringFromNetworkReachabilityStatus实现
我们能够通过对这些文字进行本地化解决,获取到的将是具备本地化信息的文字了。
3. AFNetworkReachabilityManager 结构
AFNetworkReachabilityManager 是网络监听的核心类,它是基于框架SystemConfiguration实现网络监听的。
3.1 AFNetworkReachabilityManager 初始化
AFNetworkReachabilityManager 提供了五个初始化方法和两个禁用初始化方法:
+ (instancetype)sharedManager; // 单例+ (instancetype)manager; // 自动创立的非单例+ (instancetype)managerForDomain:(NSString *)domain; // 使用指定的域名创立的非单例+ (instancetype)managerForAddress:(const void *)address; // 使用指定Socket地址创立的非单例- (instancetype)initWithReachability:(SCNetworkReachabilityRef)reachability NS_DESIGNATED_INITIALIZER; // 使用一个 SCNetworkReachabilityRef 目标引用来创立一个非单例+ (instancetype)new NS_UNAVAILABLE; - (instancetype)init NS_UNAVAILABLE;NS_UNAVAILABLE 修饰某个方法之后,我们在编码时,就不会自动显示这个方法了,假如强行使用,将会报错。这里作者将 new类方法和init 初始化方法都禁用,因而我们将不能再使用这两个方法。这个做法值得我们去借鉴。
还有一点,在一个类的的初始化方法中,假如我们希望指定开发者去调用某一个初始化方法时间,我们可以使用NS_DESIGNATED_INITIALIZER指定。 前提这个方法中会包含初始化与一个类时需要的所有参数。比方 -initWithReachability就是此类,这个方法中将包含了所有的需要的参数。
另外五个可用的方法我们从下往上看:
- (instancetype)initWithReachability:(SCNetworkReachabilityRef)reachability { self = [super init]; if (!self) { return nil; } _networkReachability = CFRetain(reachability); self.networkReachabilityStatus = AFNetworkReachabilityStatusUnknown; return self;} 上面说了,AFNetworkReachabilityManager 是基于框架 SystemConfiguration实现网络监听的,这个方法中的 SCNetworkReachabilityRef就是SystemConfiguration框架下的网络监听目标的引用。得到SCNetworkReachabilityRef引用之后,AFNetworkReachabilityManager 使用一个变量 _networkReachability 保存。
_networkReachability
注意这里,使用了CFRetain(reachability);来赋值,起因是,_networkReachability本身并不是一个Cocoa对象,所以只能使用assign来修饰,assign没有是直接持有,而CFRetain可使得assign修饰的属性使用引用计数,作用相当于强引用。使用完之后,需要使用相应的CFRelease进行释放。
再说SCNetworkReachabilityRef这个引用,它有两类创立方式,一种是将域名作为监听目标,另一种是将Scoket地址作为监听目标,它一共有三个方法:
SCNetworkReachabilityRef __nullableSCNetworkReachabilityCreateWithAddress ( CFAllocatorRef __nullable allocator, const struct sockaddr *address ) API_AVAILABLE(macos(10.3), ios(2.0));SCNetworkReachabilityRef __nullableSCNetworkReachabilityCreateWithAddressPair ( CFAllocatorRef __nullable allocator, const struct sockaddr * __nullable localAddress, const struct sockaddr * __nullable remoteAddress ) API_AVAILABLE(macos(10.3), ios(2.0));SCNetworkReachabilityRef __nullableSCNetworkReachabilityCreateWithName ( CFAllocatorRef __nullable allocator, const char *nodename ) API_AVAILABLE(macos(10.3), ios(2.0));第二种的方式和第一种其实是相似的,只是重点区分了本地的地址和远程的地址。
我们假如要使用 initWithReachability: 创立一个 AFNetworkReachabilityManager 的话,就必需使用上面的三个方法之一来构建其需要的参数了。
在AFNetworkReachabilityManager 中,用了第一种和第三种。他们在两个分别用在两个初始化的方式中:
image.png
这里注意到,假如是非 Cocoa 的框架,我们需要对其产生的对象进行释放。如上面的 CFRelease(reachability);。
我们在使用使用的,假如需要监听自家的网站域名,那么可以使用
[AFNetworkReachabilityManager managerForDomain:@"公司的服务器地址"];这个方式来监听。这样有利于我们针对自己服务器的链接状态监听。相对来说更加精准。
AFNetworkReachabilityManager 默认情况下,是使用了监听 Socket 地址的方式进行的。从这个两个初始化方式中可以看到:
manager和sharedManager
源码中,可以看到, +sharedManager 实际上是对 +manager上的一个单例,而在+manager中,最终获取的是 + managerForAddress创立的实例。这其中创立了一个sockaddr_in结构体:
sockaddr_in结构体
+manager方法中并没有指明结构体的 port 和 addr,只是指明了内部的连接协议族 sin_family 为 AF_INET,意思是这个传输方式是TCP或者者UDP等。其余的信息将会在SCNetworkReachabilityRef中补充默认值。
从这里即可以看到,当我们使用默认单例方法创立一个AFNetworkReachabilityManager 单例时,实际上就是以默认的Scoket地址作为监听的应用对象,获取网络变化。
3.2 开始监听
AFNetworkReachabilityManager 开启监听和中止监听分别由以下两个方法解决,它们的实现:
开始监听和中止监听
在开启监听的实现中,首先会中止网络监听,保证最终进入未监听的状态。而后定义了一个回调函数 callback,这个 callback 其实就是一个Block 他的类型是AFNetworkReachabilityStatusBlock 将会在网络变化时调用。callback 内最终将执行对象的 networkReachabilityStatusBlock :
image.png
我们可以通过
image.png
来设置这个对象的值。 达到往外调出的目的。
这里面还有一个很常见的技巧:作者在 block 中进行了强引用弱引用对象,目的是确保在执行block的过程中,就算对象被释放,也不会立刻销毁,保证block安全的执行完毕。
设置好回调用的 callback 之后,需要把callback 和网络的变化监听连接起来。下面这段代码的作用就是这个:
image.png
我们发现这里使用了一个新的东西 —— SCNetworkReachabilityContext结构体:
image.png
网上找到一份详细的关于SCNetworkReachabilityContext的解释:
typedef struct { CFIndex version; // 创立一个 SCNetworkReachabilityContext 结构体时,需要调用 SCDynamicStore 的创立函数,而此创立函数会根据 version 来创立出不同的结构体,SCNetworkReachabilityContext 对应的 version 是 0; void * __nullable info; // A C pointer to a user-specified block of data. 客户指定的需要传递的数据快,下面两个 block(retain 和 release)的参数就是 info。假如 info 是一个 block 类型,需要调用下面定义的 retain 和 release 进行拷贝和释放; const void * __nonnull (* __nullable retain)(const void *info); // 该 retain block 用于对上述 info 进行 retain(一般通过调用 Block_copy 宏 retain 一个 block 函数,即在堆空间新建或者直接引用一个 block 拷贝),该值可以为 NULL; void (* __nullable release)(const void *info); // 该 release block 用于对 info 进行 release(一般通过调用 Block_release 宏 release 一个 block 函数,即将 block 从堆空间移除或者移除相应引用),该值可以为 NULL; CFStringRef __nonnull (* __nullable copyDescription)(const void *info); // 提供 info 的形容,一般取为 NULL。} SCNetworkReachabilityContext;作者:XcodeMen链接:https://www.songma.com/p/fb3676a3d5f7來源:简书简书著作权归作者所有,任何形式的转载都请联络作者取得受权并注明出处。SCNetworkReachabilityContext结构体的作用是关联callback和SCNetworkReachabilityRef。两者都作为 SCNetworkReachabilitySetCallback 的入参进行关联。
但是我们最终还看到一个参数: AFNetworkReachabilityCallback ,这个其实和callback相似,但是,他不需要通过context进行承载,直接能够监听网络变化。AFNetworkReachabilityCallback的实现:
AFNetworkReachabilityCallback实现
可以看到AFNetworkReachabilityCallback是用在发送全局通知上了。
有点绕,做个简单的小总结:
AFNetworkReachabilityStatusBlock类型的callback最终被加入SCNetworkReachabilityContext中监听网络变化,用在外部获取王网络状态的 block 执行。
AFNetworkReachabilityCallback直接用于监听网络变化,获取变化后,客户发送全局通知到外部。
最后,加入监听之后,立马做了一次通知中心的发送,发送当前的网络状态:
初次发送状态状态
3.2 中止监听
中止监听就比较简单了,直接将网络监听引用从 runloop 中移除就行了。
中止监听网络
以上就是对于AFNetworkReachabilityManager的一律阅读记录。 如有错误欢迎指正。
1. 本站所有资源来源于用户上传和网络,如有侵权请邮件联系站长!
2. 分享目的仅供大家学习和交流,您必须在下载后24小时内删除!
3. 不得使用于非法商业用途,不得违反国家法律。否则后果自负!
4. 本站提供的源码、模板、插件等等其他资源,都不包含技术服务请大家谅解!
5. 如有链接无法下载、失效或广告,请联系管理员处理!
6. 本站资源售价只是摆设,本站源码仅提供给会员学习使用!
7. 如遇到加密压缩包,请使用360解压,如遇到无法解压的请联系管理员
开心源码网 » AFNetworking 源码阅读之网络监听 Reachability