这个文件是监听网络状态的,里面东西很简单,弄懂那几个结构体就行了,就不多说了,直接上代码。
typedef NS_ENUM(NSInteger, AFNetworkReachabilityStatus) {
//未知状态
AFNetworkReachabilityStatusUnknown = -1,
//无网络
AFNetworkReachabilityStatusNotReachable = 0,
//蜂窝移动网络
AFNetworkReachabilityStatusReachableViaWWAN = 1,
//WiFi
AFNetworkReachabilityStatusReachableViaWiFi = 2,
};
作者把SCNetworkReachabilityFlags
里的状态简单的封装成了常用的这四种,够大多数情况下的使用了。
/**
当前网络状态
*/
@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;
作者暴露了这四个属性供我们获取当前的网络环境。
#pragma mark 初始化方法:
/**
单例初始化
*/
+ (instancetype)sharedManager;
/**
默认socket地址的初始化
*/
+ (instancetype)manager;
/**
根据给定的域名初始化
*/
+ (instancetype)managerForDomain:(NSString *)domain;
/**
根据给定的socket地址初始化
*/
+ (instancetype)managerForAddress:(const void *)address;
/**
根据给定的可到达对象初始化
*/
- (instancetype)initWithReachability:(SCNetworkReachabilityRef)reachability NS_DESIGNATED_INITIALIZER;
需要对一个socket地址或者一个域名进行监听,是否能够发送数据包,所以初始化的时候需要一个socket地址或者域名。
/**
开启网络监听
*/
- (void)startMonitoring;
/**
停止网络监听
*/
- (void)stopMonitoring;
/**
返回当前网络可达性状态对应的本地化文本信息
*/
- (NSString *)localizedNetworkReachabilityStatusString;
/**
设置网络状态发生变化时的回调函数
*/
- (void)setReachabilityStatusChangeBlock:(nullable void (^)(AFNetworkReachabilityStatus status))block;
接下来看看具体的实现
NSString * AFStringFromNetworkReachabilityStatus(AFNetworkReachabilityStatus status) {
switch (status) {
case AFNetworkReachabilityStatusNotReachable:
return NSLocalizedStringFromTable(@"Not Reachable", @"AFNetworking", nil);
case AFNetworkReachabilityStatusReachableViaWWAN:
return NSLocalizedStringFromTable(@"Reachable via WWAN", @"AFNetworking", nil);
case AFNetworkReachabilityStatusReachableViaWiFi:
return NSLocalizedStringFromTable(@"Reachable via WiFi", @"AFNetworking", nil);
case AFNetworkReachabilityStatusUnknown:
default:
return NSLocalizedStringFromTable(@"Unknown", @"AFNetworking", nil);
}
}
这个根据网络状态获取本地化字符串函数的具体实现,这里有一个NSLocalizedStringFromTable(key, tbl, comment)
函数,是用来实现本地化的,和NSLocalizedString(key, comment)
不同的是,前者是用在自定义的tbl.string文件,后者是用在Localizable.strings文件。
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;
//不可发送设置为AFNetworkReachabilityStatusNotReachable
if (isNetworkReachable == NO) {
status = AFNetworkReachabilityStatusNotReachable;
}
#if TARGET_OS_IPHONE
//如果是蜂窝移动网络设置AFNetworkReachabilityStatusReachableViaWWAN
else if ((flags & kSCNetworkReachabilityFlagsIsWWAN) != 0) {
status = AFNetworkReachabilityStatusReachableViaWWAN;
}
#endif
//如果是wifi设置AFNetworkReachabilityStatusReachableViaWiFi
else {
status = AFNetworkReachabilityStatusReachableViaWiFi;
}
return status;
}
这个就是根据系统的网络状态SCNetworkReachabilityFlags
转换为我们自定义的网络状态AFNetworkReachabilityStatus
。
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];
});
}
static void AFNetworkReachabilityCallback(SCNetworkReachabilityRef __unused target, SCNetworkReachabilityFlags flags, void *info) {
AFPostReachabilityStatusChange(flags, (__bridge AFNetworkReachabilityStatusBlock)info);
}
第一个函数就是根据系统的网络状态方法获取自定义方法,然后用“block”和“通知”两种方式通知网络状态的变化。
第二个函数就是监听到网络状态发生变化时系统的回调,然后函数内部调用了第一个函数去实现外部通知。
然后就是几种初始化方法,自己看一下就好了;还有就是那几个网络状态属性的get方法,都很简单,也自己看下吧。
接下来就是这个类的重点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);
}
};
SCNetworkReachabilityContext context = {0, (__bridge void *)callback, AFNetworkReachabilityRetainCallback, AFNetworkReachabilityReleaseCallback, NULL};
SCNetworkReachabilitySetCallback(self.networkReachability, AFNetworkReachabilityCallback, &context);
SCNetworkReachabilityScheduleWithRunLoop(self.networkReachability, CFRunLoopGetMain(), kCFRunLoopCommonModes);
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0),^{
SCNetworkReachabilityFlags flags;
if (SCNetworkReachabilityGetFlags(self.networkReachability, &flags)) {
AFPostReachabilityStatusChange(flags, callback);
}
});
}
先把之前的监听停掉,判断监听对象是不是空,是空就结束。
callback
这个block是在网络发生变化时系统的回调,在塔的实现里调用了外部设置的网络发生变化时的blcok回调。
/*!
结构体包含了用户指定的数据和SCNetworkReachability的回调。
version 结构体的版本号,当前版本号是0,直接传0就行了
info 自定义的block函数指针
retain info的retain操作函数,可以为空
release info的release操作函数,可以为空
copyDescription 根据info获取Description字符串
*/
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;
这个结构体就是一个Reachability的上下文,我们在上下文环境中设置了Reachability发生变化时的回调,和回调函数的retain、release操作。
/*!
网络状态发生变化时的回调函数
SCNetworkReachabilityRef 监听对象
SCNetworkReachabilityFlags 网络状态
info 自定义的block函数指针
*/
typedef void (*SCNetworkReachabilityCallBack) (
SCNetworkReachabilityRef target,
SCNetworkReachabilityFlags flags,
void * __nullable info
);
/*!
设置网络状态变化时的回调
SCNetworkReachabilityRef 监听对象
SCNetworkReachabilityCallBack 网络状态发生变化时的回调函数
context 上下文环境,关联SCNetworkReachabilityCallBack回调
*/
Boolean
SCNetworkReachabilitySetCallback (
SCNetworkReachabilityRef target,
SCNetworkReachabilityCallBack __nullable callout,
SCNetworkReachabilityContext * __nullable context
)
SCNetworkReachabilitySetCallback
就是把target(监听对象)和context(上下文环境)中自定义的回调函数(AFN中就是刚刚写的callback函数)还有网络状态作为callout的参数,在网络状态发生变化时调用callout。
SCNetworkReachabilityScheduleWithRunLoop
就是把监听对象self.networkReachability
加入主线程的runloop中,用的是kCFRunLoopCommonModes伪模式。
再后面就是在子线程中主动发出当前的网络状态。
- (void)stopMonitoring {
if (!self.networkReachability) {
return;
}
SCNetworkReachabilityUnscheduleFromRunLoop(self.networkReachability, CFRunLoopGetMain(), kCFRunLoopCommonModes);
}
这个就是停止监听,很简单,把self.networkReachability
监听对象从runloop中拿出来就行了。
如有错误之处,还请不吝赐教,谢谢!