AFNetworking中提供了AFNetworkReachabilityManager类,用于应用生命周期中网络状态的监听。
AFNetworkReachabilityManager继承自NSObject
@interface AFNetworkReachabilityManager : NSObject
typedef NS_ENUM(NSInteger, AFNetworkReachabilityStatus) {
AFNetworkReachabilityStatusUnknown = -1,
AFNetworkReachabilityStatusNotReachable = 0,
AFNetworkReachabilityStatusReachableViaWWAN = 1,
AFNetworkReachabilityStatusReachableViaWiFi = 2,
};
AFNetworkReachabilityStatusUnknown 表示未知网络
AFNetworkReachabilityStatusNotReachable 表示网络未连接
AFNetworkReachabilityStatusReachableViaWWAN 表示是手机网络,2G,3G,4G
AFNetworkReachabilityStatusReachableViaWiFi 表示连接的WiFi
@property (readonly, nonatomic, assign) AFNetworkReachabilityStatus networkReachabilityStatus;
networkReachabilityStatus属性表示当前应用所处的网络状态
@property (readonly, nonatomic, assign, getter = isReachable) BOOL reachable;
reachable 属性表示当前应用是否连接了网络,这个属性的getter方法被重新自定义过,如果应用处于手机网络(WWAN)或者WiFi状态下,就返回YES,否则返回NO,如下
- (BOOL)isReachable {
return [self isReachableViaWWAN] || [self isReachableViaWiFi];
}
@property (readonly, nonatomic, assign, getter = isReachableViaWWAN) BOOL reachableViaWWAN;
reachableViaWWAN 属性表示应用当前是否处于WWAN网络状态下,如果在该网络环境下,就返回YES,否则返回NO
@property (readonly, nonatomic, assign, getter = isReachableViaWiFi) BOOL reachableViaWiFi;
reachableViaWiFi 属性表示应用当前是否处于WiFi网络状态下,如果应用处于WiFi环境下,就返回YES,否则返回NO
属性就讲到这里,消息我们就去.m文件中看看。
--------------------------------------------------------------------分割线,棒棒哒--------------------------------------------------------------------------------
@interface AFNetworkReachabilityManager ()
@property (readonly, nonatomic, assign) SCNetworkReachabilityRef networkReachability;
@property (readwrite, nonatomic, assign) AFNetworkReachabilityStatus networkReachabilityStatus;
@property (readwrite, nonatomic, copy) AFNetworkReachabilityStatusBlock networkReachabilityStatusBlock;
@end
SCNetworkReachabilityRef networkReachability; 属性
AFNetworkReachabilityStatus networkReachabilityStatus; 属性
AFNetworkReachabilityStatusBlock networkReachabilityStatusBlock; 属性,这是一个block属性,用来做网络状态连接的时候,回调这个block
+ (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];
}
来分析下+(instancetype)manager类方法:
先预编译,根据__IPHONE_OS_VERSION_MIN_REQUIRED(ios版本号) 高于 9.0,以及__MAC_OS_X_VERSION_MIN_REQUIRED (mac版本号) 高于 10.11,那么我们就必须要使用ipv6的方式,如果低于这两个系统的版本号,我们就用ipv4的方法来创建socket套接字。
+ (instancetype)managerForAddress:(const void *)address {
SCNetworkReachabilityRef reachability = SCNetworkReachabilityCreateWithAddress(kCFAllocatorDefault, (const struct sockaddr *)address);
AFNetworkReachabilityManager *manager = [[self alloc] initWithReachability:reachability];
CFRelease(reachability);
return manager;
}
managerForAddress这个类方法是根据传入的套接字来创建一个网络连接引用。
SCNetworkReachabilityRef reachability = SCNetworkReachabilityCreateWithAddress(kCFAllocatorDefault,(const struct sockaddr *)address);
这个c方法就是根据传入的参数来获取网络连接的一个引用,第一个参数可以是NULL或kCFAllocatorDefault,第二个参数就是需要测试连接的IP地址,创建好网络连接引用SCNetworkReachabilityRef使用后,注意要使用CFRelease来释放掉这个数据
上边提到用套接字作为一个网络连接引用创建的时候的参数,同样,我们可以不用套接字IP地址去做参数,我们可以提供一个域名来创建网络连接引用
+ (instancetype)managerForDomain:(NSString *)domain {
SCNetworkReachabilityRef reachability = SCNetworkReachabilityCreateWithName(kCFAllocatorDefault, [domain UTF8String]);
AFNetworkReachabilityManager *manager = [[self alloc] initWithReachability:reachability];
CFRelease(reachability);
return manager;
}
- (instancetype)initWithReachability:(SCNetworkReachabilityRef)reachability {
self = [super init];
if (!self) {
return nil;
}
_networkReachability = CFRetain(reachability);
self.networkReachabilityStatus = AFNetworkReachabilityStatusUnknown;
return self;
}
一旦我们有了SCNetworkReachabilityRef网络连接的引用,那么我们就可以使用这个连接引用去初始化我们的属性_networkReachability,并且在初始化的时候我们先将 self.networkReachabilityStatus 设置为未知网络状态。
+ (instancetype)sharedManager {
static AFNetworkReachabilityManager *_sharedManager = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
_sharedManager = [self manager];
});
return _sharedManager;
}
sharedManager方法就是使用manager来得到AFNetworkReachabilityManager单例
- (void)dealloc {
[self stopMonitoring];
if (_networkReachability != NULL) {
CFRelease(_networkReachability);
}
}
析构函数,里边的操作当然得释放资源了
- (BOOL)isReachable {
return [self isReachableViaWWAN] || [self isReachableViaWiFi];
}
- (BOOL)isReachableViaWWAN {
return self.networkReachabilityStatus == AFNetworkReachabilityStatusReachableViaWWAN;
}
- (BOOL)isReachableViaWiFi {
return self.networkReachabilityStatus == AFNetworkReachabilityStatusReachableViaWiFi;
}
返回属性,已判断应用所处网络状态
- (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);
}
});
}
我们来重点看看这个方法实现:
__weak __typeof(self)weakSelf = self;
AFNetworkReachabilityStatusBlock callback = ^(AFNetworkReachabilityStatus status) {
__strong __typeof(weakSelf)strongSelf = weakSelf;
strongSelf.networkReachabilityStatus = status;
if (strongSelf.networkReachabilityStatusBlock) {
strongSelf.networkReachabilityStatusBlock(status);
}
};
这里我们创建了一个AFNetworkReachabilityStatusBlock类型的block,在这个block里边我们执行了我们的属性block,也就是说,一旦回调callback这个block,那么相应的我们自定义的AFNetworkReachabilityStatusBlock的属性_networkReachabilityStatusBlock也会被回调。
SCNetworkReachabilityContext context = {0, (__bridge void *)callback, AFNetworkReachabilityRetainCallback, AFNetworkReachabilityReleaseCallback, NULL};
创建SCNetworkReachabilityContext context,介绍下SCNetworkReachabilityContext结构内容typedef struct {
CFIndex version;
void * info;
const void *(*retain)(const void *info);
void (*release)(const void *info);
CFStringRef (*copyDescription)(const void *info);
} SCNetworkReachabilityContext;
这个结构体中涉及到的几个参数
CFIndex version; 0
void * info; 就是上边创建的callback
const void * (*retain)(const void * info); 返回任意指针类型,参数是一个指针,的指针函数
static const void * AFNetworkReachabilityRetainCallback(const void *info) {
return Block_copy(info);
}
void (*release)(const void * info);
static void AFNetworkReachabilityReleaseCallback(const void *info) {
if (info) {
Block_release(info);
}
}
CFStringRef (*copyDescription) (const void *info); NULL
SCNetworkReachabilitySetCallback(self.networkReachability, AFNetworkReachabilityCallback, &context);
试着给self.networkReachability这个网络连接引用设置回调方法,如果self.networkReachability这个网络连接引用发生改变,那么AFNetworkReachabilityCallBack就会回调
static void AFNetworkReachabilityCallback(SCNetworkReachabilityRef __unused target, SCNetworkReachabilityFlags flags, void *info) {
AFPostReachabilityStatusChange(flags, (__bridge AFNetworkReachabilityStatusBlock)info);
}
一旦self.networkReachability发生改变,上边的方法就会被调用,所以AFPostReachabilityStatusChange();方法就会被调用
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];
});
}
然后根据flags得到网络连接状态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);
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 {
status = AFNetworkReachabilityStatusReachableViaWiFi;
}
return status;
}
然后异步执行callback回调方法,自然的我们自定义的block属性也被调用了,上边解释过了
最后发送AFNetworkingReachabilityDidChangeNotification通知,这个通知携带了网络状态情况
SCNetworkReachabilityScheduleWithRunLoop(self.networkReachability, CFRunLoopGetMain(), kCFRunLoopCommonModes);
这句代码就是将网络连接引用状态的变化监听放入MainRunLoop中,为了在应用整个生命周期中,都能够得到检测和响应。
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0),^{
SCNetworkReachabilityFlags flags;
if (SCNetworkReachabilityGetFlags(self.networkReachability, &flags)) {
AFPostReachabilityStatusChange(flags, callback);
}
});
先异步执行一个网络连接引用状态是否改变的监听
- (void)stopMonitoring {
if (!self.networkReachability) {
return;
}
SCNetworkReachabilityUnscheduleFromRunLoop(self.networkReachability, CFRunLoopGetMain(), kCFRunLoopCommonModes);
}
停止网络连接引用状态的监听,从MainRunLoop中删除掉
- (void)setReachabilityStatusChangeBlock:(void (^)(AFNetworkReachabilityStatus status))block {
self.networkReachabilityStatusBlock = block;
}
自定义我们的block内容,给self.networkReachabilityStatusBlock赋予我们想要的操作,以至于满足项目需求
关于AFNetworkReachabilityManager的分析就讲到这里,以后遇到实际应用,再回来补充实际使用情况......