iOS源码解析—AFNetworking(AFNetworkReachabilityManager)

来源:互联网 发布:freebsd 编译安装软件 编辑:程序博客网 时间:2024/05/06 01:14

概述

在AFN框架中,AFURLSessionManager对象的初始化方法中创建了AFNetworkReachabilityManager对象用于监听设备当前连接网络的状态。文本分析一下AFNetworkReachabilityManager。

初始化方法

AFNetworkReachabilityManager提供了4种创建方法,如下:

+ (instancetype)sharedManager; //创建单例对象+ (instancetype)manager; //创建实例对象+ (instancetype)managerForDomain:(NSString *)domain; //根据地址名创建实例对象+ (instancetype)managerForAddress:(const void *)address; //根据sockaddr创建实例对象

第一个方法创建单例对象,代码注释如下:

+ (instancetype)sharedManager {    static AFNetworkReachabilityManager *_sharedManager = nil;    static dispatch_once_t onceToken;    dispatch_once(&onceToken, ^{        _sharedManager = [self manager]; //调用manager方法创建对象    });    return _sharedManager;}

sharedManager调用manager方法创建一个AFNetworkReachabilityManager对象,代码注释如下:

+ (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; //创建一个ipv6类型的地址结构体    bzero(&address, sizeof(address));    address.sin6_len = sizeof(address);    address.sin6_family = AF_INET6;#else    struct sockaddr_in address; //创建一个ipv4类型的地址结构体    bzero(&address, sizeof(address));    address.sin_len = sizeof(address);    address.sin_family = AF_INET;#endif    return [self managerForAddress:&address]; //根据地址信息创建对象}

其中sockaddr_in6和sockaddr_in是描述网络套接字的结构体,包含协议族类型、端口、ip地址等信息,然后调用managerForAddress:方法创建,代码注释如下:

+ (instancetype)managerForAddress:(const void *)address {    SCNetworkReachabilityRef reachability = SCNetworkReachabilityCreateWithAddress(kCFAllocatorDefault, (const struct sockaddr *)address); //创建SCNetworkReachabilityRef对象    AFNetworkReachabilityManager *manager = [[self alloc] initWithReachability:reachability]; //创建AFNetworkReachabilityManager对象,初始还参数    return manager;}

该方法的核心代码是通过SCNetworkReachabilityCreateWithAddress方法创建一个SCNetworkReachabilityRef对象reachability,该对象负责监听address的网络状态,address参数是需要监听的地址信息,由于address参数的ip地址为空,则reachability对象监听当前设备的网络连接状态。另一个方法managerForDomain:是通过传进来的domain网络地址名,例如www.baidu.com来创建。代码注释如下:

+ (instancetype)managerForDomain:(NSString *)domain {    //根据domain创建SCNetworkReachabilityRef对象    SCNetworkReachabilityRef reachability = SCNetworkReachabilityCreateWithName(kCFAllocatorDefault, [domain UTF8String]);    AFNetworkReachabilityManager *manager = [[self alloc] initWithReachability:reachability]; //创建AFNetworkReachabilityManager对象,初始还参数    return manager;}

SCNetworkReachabilityCreateWithName方法和上面的类似,也是用于创建一个SCNetworkReachabilityRef对象。调用initWithReachability:方法,初始化参数,代码注释如下:

- (instancetype)initWithReachability:(SCNetworkReachabilityRef)reachability {    self = [super init];    if (!self) {        return nil;    }    self.networkReachability = CFBridgingRelease(reachability); //初始化networkReachability属性    self.networkReachabilityStatus = AFNetworkReachabilityStatusUnknown; //初始化连接状态属性    return self;}

networkReachability属性持有reachability,networkReachabilityStatus属性表示当前连接的网络状态,共有以下几种:

typedef NS_ENUM(NSInteger, AFNetworkReachabilityStatus) {    AFNetworkReachabilityStatusUnknown          = -1,//未知状态    AFNetworkReachabilityStatusNotReachable     = 0, //未连接    AFNetworkReachabilityStatusReachableViaWWAN = 1, //蜂窝移动网络(2G/3G/4G)    AFNetworkReachabilityStatusReachableViaWiFi = 2, //wifi网络};

上面的几个枚举值表示当前检测到的网络状态。

监听网络状态

AFNetworkReachabilityManager通过startMonitoring方法和stopMonitoring开始并停止监听当前设备连接的网络状态。

  1. startMonitoring方法

    该方法主要通过SystemConfiguration框架提供的API将networkReachability让对象加入runloop中,开始工作,并且绑定监听的回调函数处理状态改变。代码注释如下:

    - (void)startMonitoring {   [self stopMonitoring]; //1.停止之前的监听   if (!self.networkReachability) {       return;   }//2.创建context的block   __weak __typeof(self)weakSelf = self;   AFNetworkReachabilityStatusBlock callback = ^(AFNetworkReachabilityStatus status) {       __strong __typeof(weakSelf)strongSelf = weakSelf;       strongSelf.networkReachabilityStatus = status;       if (strongSelf.networkReachabilityStatusBlock) {           strongSelf.networkReachabilityStatusBlock(status);       }   };   id networkReachability = self.networkReachability;    //创建SCNetworkReachabilityContext对象   SCNetworkReachabilityContext context = {0, (__bridge void *)callback, AFNetworkReachabilityRetainCallback, AFNetworkReachabilityReleaseCallback, NULL};    //3.设置networkReachability的回调函数和context   SCNetworkReachabilitySetCallback((__bridge SCNetworkReachabilityRef)networkReachability, AFNetworkReachabilityCallback, &context);    //4.将networkReachability加入runloop中   SCNetworkReachabilityScheduleWithRunLoop((__bridge SCNetworkReachabilityRef)networkReachability, CFRunLoopGetMain(), kCFRunLoopCommonModes);//5.获取当前网络连接状态dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0),^{SCNetworkReachabilityFlags flags;if (SCNetworkReachabilityGetFlags((__bridge SCNetworkReachabilityRef)networkReachability, &flags)) {    AFPostReachabilityStatusChange(flags, callback); //执行block,发通知}   });}

    该方法首先停止之前的监听,然后调用SCNetworkReachabilitySetCallback方法来设置networkReachability的回调函数AFNetworkReachabilityCallback和上下文context对象,该方法的定义和参数如下:

    BooleanSCNetworkReachabilitySetCallback     (SCNetworkReachabilityRef        target, //networkReachability对象SCNetworkReachabilityCallBack   __nullable callout, //回调方法SCNetworkReachabilityContext    * __nullable context //上下文兑现)

    首先target对象是要绑定的networkReachability对象,即networkReachability属性,callout是networkReachability对象监听到网络状态发生改变时,触发的回调函数,类型是SCNetworkReachabilityCallBack,定义如下:

    typedef void (*SCNetworkReachabilityCallBack)    (SCNetworkReachabilityRef            target, //networkReachability对象SCNetworkReachabilityFlags          flags,  //回调参数,状态值void                 *  __nullable  info    //info函数指针);

    该函数回抛target对象,即之前绑定的networkReachability对象。以及flag标识,通过flag可以获取当前网络状态值。以及info指针,指向一个block。其中info是由context提供的,context是SCNetworkReachabilityContext类型的结构体,定义如下:

    typedef struct {CFIndex     version;void *      __nullable info; //info函数指针const void  * __nonnull (* __nullable retain)(const void *info); //retain函数指针void        (* __nullable release)(const void *info); //release函数指针CFStringRef __nonnull (* __nullable copyDescription)(const void *info); //获取info的Description的函数指针} SCNetworkReachabilityContext;

    其中info函数指针提供给上文的回调函数使用,作为回调参数info。info是AFNetworkReachabilityStatusBlock类型,需要一个回调参数status,创建info的代码如下:

    __weak __typeof(self)weakSelf = self;   AFNetworkReachabilityStatusBlock callback = ^(AFNetworkReachabilityStatus status) {       __strong __typeof(weakSelf)strongSelf = weakSelf;       strongSelf.networkReachabilityStatus = status; //设置状态值       if (strongSelf.networkReachabilityStatusBlock) { //将状态值通过block回抛给外界           strongSelf.networkReachabilityStatusBlock(status);       }};

    该函数负责将获取到的网络状态值status通过networkReachabilityStatusBlock回抛给外界。retain函数指针和release函数指针也分别设置了。

    callout的方法如下:

    static void AFNetworkReachabilityCallback(SCNetworkReachabilityRef __unused target, SCNetworkReachabilityFlags flags, void *info) {   AFPostReachabilityStatusChange(flags, (__bridge AFNetworkReachabilityStatusBlock)info); //调用AFPostReachabilityStatusChange方法}

    该方法调用AFPostReachabilityStatusChange方法做逻辑处理,注释如下:

    static void AFPostReachabilityStatusChange(SCNetworkReachabilityFlags flags, AFNetworkReachabilityStatusBlock block) {   AFNetworkReachabilityStatus status = AFNetworkReachabilityStatusForFlags(flags); //根据flags获取当前网络连接状态status   dispatch_async(dispatch_get_main_queue(), ^{       if (block) {           block(status); //block是context中的info指针,调用info将status传递给外界       }        //status作为通知的值,发通知抛给外界       NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter];       NSDictionary *userInfo = @{ AFNetworkingReachabilityNotificationStatusItem: @(status) };       [notificationCenter postNotificationName:AFNetworkingReachabilityDidChangeNotification object:nil userInfo:userInfo];   });}

    该方法首先根据系统回调的flags参数和AFNetworkReachabilityStatusForFlags方法,获取网络连接状态status,然后调用block,即之前context中的info指针,将status抛给外界。同时抛一个通知将status抛给外界。因此当网络状态改变时,会同时用两种方式传递给外界。

    AFNetworkReachabilityStatusForFlags方法是核心方法,负责根据flag的状态值,转化为相应的枚举值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) {//当前连接是WWAN       status = AFNetworkReachabilityStatusReachableViaWWAN;   }#endif   else { //当前连接是WiFi       status = AFNetworkReachabilityStatusReachableViaWiFi;   }   return status;}

    最后在startMonitoring方法中还调用了SCNetworkReachabilityGetFlags方法获取当前网络状态,传递给外界。因为之前的设置是在网络状态发生变化时触发的。

  2. stopMonitoring方法

    通知监听的方法是让networkReachability对象从runloop中注销。代码注释如下:

    - (void)stopMonitoring {   if (!self.networkReachability) {       return;   }   //从runloop中注销networkReachability对象   SCNetworkReachabilityUnscheduleFromRunLoop((__bridge SCNetworkReachabilityRef)self.networkReachability, CFRunLoopGetMain(), kCFRunLoopCommonModes);}

读取网络状态

AFNetworkReachabilityManager维护了一些网络状态属性,如下:

@property (readonly, nonatomic, assign) AFNetworkReachabilityStatus networkReachabilityStatus;@property (readonly, nonatomic, assign, getter = isReachable) BOOL reachable;@property (readonly, nonatomic, assign, getter = isReachableViaWWAN) BOOL reachableViaWWAN;@property (readonly, nonatomic, assign, getter = isReachableViaWiFi) BOOL reachableViaWiFi;

外界通过isReachable方法、isReachableViaWWAN方法、isReachableViaWiFi方法和networkReachabilityStatus属性可以获取当前的网络状态。同时通过实现keyPathsForValuesAffectingValueForKey:方法设置属性值的依赖关系。当reachable、reachableViaWWAN、reachableViaWiFi这些属性的值发生变化时,会触发networkReachabilityStatus属性的kvo,如果外界通过kvo监听networkReachabilityStatus属性的变化,这时会触发kvo。

总结

AFNetworkReachabilityManager作为一个监听设备网络连接状态的类,核心是使用了systemConfiguration框架中的SCNetworkReachability相关类和API实现的,值得学习和了解。

0 0