学习iOS,对AFN这个框架肯定不会陌生,一直以来都是对AFN框架的一个上层封装使用,并没有进行过深入的研究,今天心血来潮,决定对其深入的研究一番。
首先观察AFNetworking头文件。其中结构为
#define _AFNETWORKING_
#import "AFURLRequestSerialization.h"
#import "AFURLResponseSerialization.h"
#import "AFSecurityPolicy.h"
#import "AFNetworkReachabilityManager.h"
#import "AFURLSessionManager.h"
#import "AFHTTPSessionManager.h"
其中主要包含这6类,我们用到的最多的当然应该是后面两个,然后分别点进对应的头文件,我们可以观察到前面4个类里面用到的都是系统的头文件,也就是说可以单独使用,那么我们今天就对AFNetworkReachabilityManager这个最简单的类先着手进行研究,我们主要是对这个开源的网络框架解读,使用,并看能在其中学到一些好的开发思路。那我们现在开始吧。点到AFNetworkReachabilityManager.h头文件中,从类名我们可以知道这个类的主要作用是检测网络状态,其中苹果也提供了相应的框架Reachability,下载地址为(https://developer.apple.com/library/ios/samplecode/reachability/ ),我们先研究AFN的这个,
#import
#if !TARGET_OS_WATCH
#import
typedef NS_ENUM(NSInteger, AFNetworkReachabilityStatus) {
AFNetworkReachabilityStatusUnknown = -1,
AFNetworkReachabilityStatusNotReachable = 0,
AFNetworkReachabilityStatusReachableViaWWAN = 1,
AFNetworkReachabilityStatusReachableViaWiFi = 2,
};
定义4中网络状态,Unknow(未知),NotReachable(不能链接),ViaWWAN(3g,4g),ViaWiFi(wifi链接)。我们的在开发的过程中定义这种常量的时候最好也能够实用相应的枚举常量,这样的可以使开发者减少沟通成本。
/**
The current network reachability status.
*/
@property (readonly, nonatomic, assign) AFNetworkReachabilityStatus networkReachabilityStatus;
/**
Whether or not the network is currently reachable.
*/
@property (readonly, nonatomic, assign, getter = isReachable) BOOL reachable;
/**
Whether or not the network is currently reachable via WWAN.
*/
@property (readonly, nonatomic, assign, getter = isReachableViaWWAN) BOOL reachableViaWWAN;
/**
Whether or not the network is currently reachable via WiFi.
*/
@property (readonly, nonatomic, assign, getter = isReachableViaWiFi) BOOL reachableViaWiFi;
定了4个常用属性,均为readonly,也就是我们作为使用AFN的开发,只能获得这几个值,(我们在开发的过程中,如果不想外界修改我对应的某个属性的时候最好也这样,后面3个均为BOOL值,均提供了getter方法,值得学习)。
其中
1.networkReachabilityStatus也就是上面定义的枚举,可以判断当前设备使一个什么网络。
2.reachable这个属性是检测是否有网,我们点进去看一下实现
- (BOOL)isReachable {
return [self isReachableViaWWAN] || [self isReachableViaWiFi];
}
- (BOOL)isReachableViaWWAN {
return self.networkReachabilityStatus == AFNetworkReachabilityStatusReachableViaWWAN;
}
- (BOOL)isReachableViaWiFi {
return self.networkReachabilityStatus == AFNetworkReachabilityStatusReachableViaWiFi;
}
这样三个属性我们就能在一起看了,可以看出就是对于上面的一些取得的属性的判断。那么对应的属性他是怎么取得的呢?接着往下看。
/**
Returns the shared network reachability manager.
*/
+ (instancetype)sharedManager;
/**
Creates and returns a network reachability manager with the default socket address.
@return An initialized network reachability manager, actively monitoring the default socket address.
*/
+ (instancetype)manager;
/**
Creates and returns a network reachability manager for the specified domain.
@param domain The domain used to evaluate network reachability.
@return An initialized network reachability manager, actively monitoring the specified domain.
*/
+ (instancetype)managerForDomain:(NSString *)domain;
/**
Creates and returns a network reachability manager for the socket address.
@param address The socket address (`sockaddr_in6`) used to evaluate network reachability.
@return An initialized network reachability manager, actively monitoring the specified socket address.
*/
+ (instancetype)managerForAddress:(const void *)address;
/**
Initializes an instance of a network reachability manager from the specified reachability object.
@param reachability The reachability object to monitor.
@return An initialized network reachability manager, actively monitoring the specified reachability.
*/
- (instancetype)initWithReachability:(SCNetworkReachabilityRef)reachability NS_DESIGNATED_INITIALIZER;
提供了5种构造方法,其中4个类方法,一个对象方法。我们一个一个点击进去了解。
首先是sharedManager这个方法。我们点击进去看到(单例实现)并调用[self manager]
+ (instancetype)sharedManager {
static AFNetworkReachabilityManager *_sharedManager = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
_sharedManager = [self manager];
});
return _sharedManager;
}
单粒的实现我们也可以这样去做,接着那 manager中又是怎么回事呢?
+ (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];
}
manager中的实现是这样的,看到这我们就不得说到另外一个苹果提供的SCNetworkReachability
这个对象了(我们主要知道其中的2个东西就可以了 SCNetworkReachabilityRef
以及SCNetworkReachabilityFlags
),
-SCNetworkReachabilityRef 就是测试连接,SCNetworkReachabilityFlags就是返回的状态。
其实AFNetworkReachabilityManager主要就是对这个类的进一层封装,SCNetworkReachability在SystemConfiguration这个框架中,所以前面才会引用SystemConfiguration/SystemConfiguration.h这个头文件,过程是这样,创建测试链接引用,拿到链接状态,保存返回的链接状态(这就是我对于AFNetworkReachabilityManager所做事情的理解)。SCNetworkReachability创建链接方式(SCNetworkReachabilityRef)苹果提供了3种,AFN中用到了2种
3种方式分别是
/*
You must release the returned value.
*/
SCNetworkReachabilityRef __nullable
SCNetworkReachabilityCreateWithAddress (
CFAllocatorRef __nullable allocator,
const struct sockaddr *address
) __OSX_AVAILABLE_STARTING(__MAC_10_3,__IPHONE_2_0);
SCNetworkReachabilityRef __nullable
SCNetworkReachabilityCreateWithAddressPair (
CFAllocatorRef __nullable allocator,
const struct sockaddr * __nullable localAddress,
const struct sockaddr * __nullable remoteAddress
) __OSX_AVAILABLE_STARTING(__MAC_10_3,__IPHONE_2_0);
SCNetworkReachabilityRef __nullable
SCNetworkReachabilityCreateWithName (
CFAllocatorRef __nullable allocator,
const char *nodename
) __OSX_AVAILABLE_STARTING(__MAC_10_3,__IPHONE_2_0);
SCNetworkReachabilityCreateWithAddress和SCNetworkReachabilityCreateWithAddressPair均为用ip地址创建链接,后者需要传一个本地ip,一个远程ip,第三个传一个名称以建立一个链接(特别声明You must release the returned value.)你必须在建立完链接之后手动释放。
看完之后在看上面的manager代码,可以看到if else中的代码是定义一个地址,bzero(&address, sizeof(address));这句代码的意思是 创建零地址,0.0.0.0的地址表示查询本机的网络连接状态,然后内部在调用managerForAddress:&address这句代码,那我们看在managerForAddress中又做了什么呢?
+ (instancetype)managerForAddress:(const void *)address {
SCNetworkReachabilityRef reachability = SCNetworkReachabilityCreateWithAddress(kCFAllocatorDefault, (const struct sockaddr *)address);
AFNetworkReachabilityManager *manager = [[self alloc] initWithReachability:reachability];
CFRelease(reachability);
return manager;
}
我们可以看到他确实是用地址创建的链接,然后在调用自身的init方法中CFRetain(reachability),在两个创建方法中分别CFRelease(reachability)了这个对象。
- (instancetype)initWithReachability:(SCNetworkReachabilityRef)reachability {
self = [super init];
if (!self) {
return nil;
}
_networkReachability = CFRetain(reachability);
self.networkReachabilityStatus = AFNetworkReachabilityStatusUnknown;
return self;
}
剩余的那个用名称创建链接的方法+ (instancetype)managerForDomain:(NSString *)domain大同小异,也就不多说了,这样5个构造方法我们就解释完了,也就是在构造方法完成之后我们得到的manager对象,并且保存了一份SCNetworkReachabilityRef对象,接下来是这个类最重要的方法:
/**
Starts monitoring for changes in network reachability status.
*/
- (void)startMonitoring;
/**
Stops monitoring for changes in network reachability status.
*/
- (void)stopMonitoring;
开始监测:
- (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);
}
});
}
开始监测的时候,先停止上一次监测,然后监测当前的SCNetworkReachabilityRef是否存在,我们的init和类方法中都创建过这个对象,然后创建一个block回掉函数,接下来这些函数就是核心,
第一句代码:
SCNetworkReachabilityContext context = {0, (__bridge void *)callback, AFNetworkReachabilityRetainCallback, AFNetworkReachabilityReleaseCallback, NULL};
点进去看函数
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;
SCNetworkReachabilityContext是一个结构体,一般c语言的结构体是对要保存的数据的一种描述
第一个参数接受一个signed long 的参数 ,第二个参数接受一个void * 类型的值,相当于oc的id类型,void * 可以指向任何类型的参数,第三个参数 是一个函数 目的是对info做retain操作,第四个参数是一个函数,目的是对info做release操作第五个参数是 一个函数,根据info获取Description字符串,
static const void * AFNetworkReachabilityRetainCallback(const void *info) {
return Block_copy(info);
}
static void AFNetworkReachabilityReleaseCallback(const void *info) {
if (info) {
Block_release(info);
}
}
两个callback函数对info的保存
拿到SCNetworkReachabilityContext,然后用我们保存的链接设置一个回掉函数,
第二句代码
SCNetworkReachabilitySetCallback(self.networkReachability, AFNetworkReachabilityCallback, &context);
Boolean
SCNetworkReachabilitySetCallback (
SCNetworkReachabilityRef target,
SCNetworkReachabilityCallBack __nullable callout,
SCNetworkReachabilityContext * __nullable context
) __OSX_AVAILABLE_STARTING(__MAC_10_3,__IPHONE_2_0);
回掉函数
static void AFNetworkReachabilityCallback(SCNetworkReachabilityRef __unused target, SCNetworkReachabilityFlags flags, void *info) {
AFPostReachabilityStatusChange(flags, (__bridge AFNetworkReachabilityStatusBlock)info);
}
三个参数 我们传的是之前保存的链接,callback调用函数,和上面创建的上下文。
接下来加入到主运行形环中,这样就可以时刻监听到当前的网络状态并做一些操作,
第三句代码
SCNetworkReachabilityScheduleWithRunLoop(self.networkReachability, CFRunLoopGetMain(), kCFRunLoopCommonModes);
Boolean
SCNetworkReachabilityScheduleWithRunLoop (
SCNetworkReachabilityRef target,
CFRunLoopRef runLoop,
CFStringRef runLoopMode
) __OSX_AVAILABLE_STARTING(__MAC_10_3,__IPHONE_2_0);
之后我们终于能够拿到SCNetworkReachabilityFlags,
SCNetworkReachabilityGetFlags(self.networkReachability, &flags)也是返回一个BOOL值,当返回YES的时候进入
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];
});
}
第一句代码 调用了AFNetworkReachabilityStatusForFlags(SCNetworkReachabilityFlags flags) 这个函数,把拿到的flag转成AFN中对应的那4个枚举值
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;
}
然后把转之后的得到枚举值用之前定义的block保存起来,并且注册一个通知,当外面添加这个通知的时候,当监测到flag改变就会发出通知让外界知道,或者通过block的方式告诉外界,也就是.h中提供的方法
- (void)setReachabilityStatusChangeBlock:(nullable void (^)(AFNetworkReachabilityStatus status))block这个方法。
注册的通知名称为
AFNetworkingReachabilityDidChangeNotification =@”com.alamofire.networking.reachability.change”; 并且还有一个通知模型,他在发送通知的时候一起将模型也发送了出去(这一点我们也有学习的必要)
然后停止监测,并移除runloop。
SCNetworkReachabilityUnscheduleFromRunLoop(self.networkReachability, CFRunLoopGetMain(), kCFRunLoopCommonModes);
最后一个方法是
- (NSString *)localizedNetworkReachabilityStatusString;与
FOUNDATION_EXPORT NSString * AFStringFromNetworkReachabilityStatus(AFNetworkReachabilityStatus status);都是把状态的枚举值转换成字符串的方法
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);
}
}
并用到了本地化文件。
这样至此我们将AFNetworkReachabilityManager方法与属性及实现全部分析完毕,总结一下就是会调用系统内部的SCNetworkReachability框架并创建链接SCNetworkReachabilityRef然后得到SCNetworkReachabilityGetFlags状态,拿到SCNetworkReachabilityGetFlags状态专程对应的枚举值保存,并在监测到值发生改变的时候,然后发出通知,或者block的方式让外界知道。之后下面会继续研究AFN的其他类的用法。