iOS源码解析—AFNetworking(AFNetworkReachabilityManager)

概述

在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对象,该方法的定义和参数如下:

    Boolean
    SCNetworkReachabilitySetCallback     (
    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
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值