iOS第三方库—源码分析iOS Developer

iOS源码解析—AFNetworking(AFNetworkRe

2017-04-07  本文已影响553人  egoCogito_panf

概述

在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实现的,值得学习和了解。

上一篇下一篇

猜你喜欢

热点阅读