内容字号:默认大号超大号

段落设置:段首缩进取消段首缩进

字体设置:切换到微软雅黑切换到宋体

iOS源码补完计划:AFNetworking(二)

2018-06-08 19:54 出处:清屏网 人气: 评论(0

目录

  • 前言
  • AFNetworkReachabilityManager.h
    • 状态枚举
    • 四个属性
    • 初始化方法
    • 开始和关闭
    • 返回网络状态的字符串
    • 网络状态改变时的回调
  • AFNetworkReachabilityManager.m
    • 初始化
    • 开始监听
    • 将系统SCNetworkReachabilityFlags转化成AF对外的网络状态枚举AFNetworkReachabilityStatus
    • 注册键值依赖
  • API注释Demo
  • 参考资料

前言

AFNetworking源码第二篇

主要看了看 AFNetworkReachabilityManager 的内容

作为一个辅助模块、代码量和文件都比较少

一行一行读下来就可以了

AFN概述: 《iOS源码补完计划--AFNetworking 3.1.0源码研读》

AFNetworkReachabilityManager.h

  • 状态枚举

我们平时使用的枚举、就是这四个。

//网络状态
typedef NS_ENUM(NSInteger, AFNetworkReachabilityStatus) {
    AFNetworkReachabilityStatusUnknown          = -1,//未知
    AFNetworkReachabilityStatusNotReachable     = 0,//无网络
    AFNetworkReachabilityStatusReachableViaWWAN = 1,//运营商网络
    AFNetworkReachabilityStatusReachableViaWiFi = 2,//WiFi网络
};
  • 四个属性

也可以单独判断是否在某个环境下

均为只读

/**
    当前网络状态
 */
@property (readonly, nonatomic, assign) AFNetworkReachabilityStatus networkReachabilityStatus;

/**
    当前是否有网络
 */
@property (readonly, nonatomic, assign, getter = isReachable) BOOL reachable;

/**
    当前是否为运营商网络
 */
@property (readonly, nonatomic, assign, getter = isReachableViaWWAN) BOOL reachableViaWWAN;

/**
    当前是否为WiFi
 */
@property (readonly, nonatomic, assign, getter = isReachableViaWiFi) BOOL reachableViaWiFi;
  • 初始化方法

/**
    全局单例
 */
+ (instancetype)sharedManager;

/**
    工厂方法
 */
+ (instancetype)manager;

/**
    监控指定网址的管理器
 */
+ (instancetype)managerForDomain:(NSString *)domain;

/**
    监听某个IP的管理器
 */
+ (instancetype)managerForAddress:(const void *)address;

/**
    通过SCNetworkReachabilityRef对象初始化、其余所有的方法也会汇聚于此
 */
- (instancetype)initWithReachability:(SCNetworkReachabilityRef)reachability NS_DESIGNATED_INITIALIZER;

最后一个 initWithReachability: 是推荐的方法、因为其他所有的方法最后内部都是通过它初始化

  • 开始和关闭

/**
    打开监听
 */
- (void)startMonitoring;

/**
    关闭监听
 */
- (void)stopMonitoring;
  • 返回网络状态的字符串

大概是方便开发者获取并且直接提示给用户吧

- (NSString *)localizedNetworkReachabilityStatusString;
  • 网络状态改变时的回调

1、通过block:

- (void)setReachabilityStatusChangeBlock:(nullable void (^)(AFNetworkReachabilityStatus status))block;

2、通过监听

FOUNDATION_EXPORT NSString * const AFNetworkingReachabilityDidChangeNotification;
FOUNDATION_EXPORT NSString * const AFNetworkingReachabilityNotificationStatusItem;

关于 FOUNDATION_EXPORTUIKIT_EXTERN 的选择:

有人说是如果文件基于 FOUNDATION 则用前者、反之则用后者。

二者都能替代 #define 、并且通过地址比对常量( 也就是可以通过 == 直接进行比较 )、效率更高。

AFNetworkReachabilityManager.m

  • 初始化

所有的初始化最后都归结于这里:用 SCNetworkReachabilityRef 对象初始化

- (instancetype)initWithReachability:(SCNetworkReachabilityRef)reachability {
    self = [super init];
    if (!self) {
        return nil;
    }

    _networkReachability = CFRetain(reachability);
    self.networkReachabilityStatus = AFNetworkReachabilityStatusUnknown;

    return self;
}

基于它的两个工厂方法

+ (instancetype)managerForDomain:(NSString *)domain {
    
    /*
         SCNetworkReachabilityRef SCNetworkReachabilityCreateWithName (     //根据传入的网址创建网络连接引用
         CFAllocatorRef allocator,                  //可以为NULL或kCFAllocatorDefault
         const char *nodename                       //比如为"www.baidu.com",此参数为域名
     
         );
         这个是根据传入的网址测试连接,
         第一个参数 可以为NULL或kCFAllocatorDefault,
         第二个参数比如为"www.apple.com",其他和上一个一样。
     */
    SCNetworkReachabilityRef reachability = SCNetworkReachabilityCreateWithName(kCFAllocatorDefault, [domain UTF8String]);

    AFNetworkReachabilityManager *manager = [[self alloc] initWithReachability:reachability];
    
    CFRelease(reachability);

    return manager;
}

+ (instancetype)managerForAddress:(const void *)address {
    /*
         SCNetworkReachabilityRef SCNetworkReachabilityCreateWithAddress (    //根据传入的地址创建网络连接引用
         CFAllocatorRef allocator,                  //可以为NULL或kCFAllocatorDefault
         const struct sockaddr *address            //需要测试连接的IP地址
     
         );
         第一个参数 可以为NULL或kCFAllocatorDefault,
         第二个参数 为需要测试连接的IP地址,当为0.0.0.0时则可以查询本机的网络连接状态。
     */
    SCNetworkReachabilityRef reachability = SCNetworkReachabilityCreateWithAddress(kCFAllocatorDefault, (const struct sockaddr *)address);
    AFNetworkReachabilityManager *manager = [[self alloc] initWithReachability:reachability];

    CFRelease(reachability);
    
    return manager;
}

需要注意的是初始化所用的 SCNetworkReachabilityRef 引用、必须在使用后CFRelease()

+ (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];
}

这个似乎涉及到了socket、等我研究研究补上~

再有就是关于 #if - #esle - #endif

其实用普通的 if-else 也是一样、好处就是在编译阶段是否会被编译。

不过、 #if - #esle - #endif 不能用来判断一个动态的语法。

  • 开始监听

- (void)startMonitoring {
    [self stopMonitoring];

    if (!self.networkReachability) {
        return;
    }

    
    __weak __typeof(self)weakSelf = self;
    //网络状态改变的时候、执行这个block
    //为什么这么写?因为AFPostReachabilityStatusChange会返回我们需要的status、但必须要一个block接受
    //并没什么特殊的含义、单纯的方便接受status罢了
    AFNetworkReachabilityStatusBlock callback = ^(AFNetworkReachabilityStatus status) {
        __strong __typeof(weakSelf)strongSelf = weakSelf;
        //改变manager的网络状态枚举
        strongSelf.networkReachabilityStatus = status;
        //调用用户传入的block
        if (strongSelf.networkReachabilityStatusBlock) {
            strongSelf.networkReachabilityStatusBlock(status);
        }
    };

    /*
         typedef struct {
         //接受一个signed long 的参数
         CFIndex        version;
         //接受一个void * 类型的值,相当于oc的id类型,void * 可以指向任何类型的参数
         void *        __nullable info;
         //接受一个函数 目的是对info做retain操作,
         const void    * __nonnull (* __nullable retain)(const void *info);
         //接受一个函数,目的是对info做release操作
         void        (* __nullable release)(const void *info);
         //接受一个函数,根据info获取Description字符串
         CFStringRef    __nonnull (* __nullable copyDescription)(const void *info);
         } SCNetworkReachabilityContext;
     
         对于这些参数、都不是必须传一个block或者怎样。如果info不是block、那么后面的reatin啥的都可以忽略
         SCNetworkReachabilityContext context = {0, (__bridge void *)(self), NULL, NULL, NULL};
     */
    //新建上下文
    SCNetworkReachabilityContext context = {0, (__bridge void *)callback, AFNetworkReachabilityRetainCallback, AFNetworkReachabilityReleaseCallback, NULL};
    
    //设置回调 -- 这里 AFNetworkReachabilityCallback 可以上面的info、也就是callback那个block 进行处理
    SCNetworkReachabilitySetCallback(self.networkReachability, AFNetworkReachabilityCallback, &context);
    //放入RunLoop池
    SCNetworkReachabilityScheduleWithRunLoop(self.networkReachability, CFRunLoopGetMain(), kCFRunLoopCommonModes);

    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0),^{
        SCNetworkReachabilityFlags flags;
        if (SCNetworkReachabilityGetFlags(self.networkReachability, &flags)) {
            //网络状态改变--去获取当前网络状态。然后发通知、调用block
            AFPostReachabilityStatusChange(flags, callback);
        }
    });
}

我对 SCNetworkReachability 没啥研究、只能一遍查资料一遍顺便注释一下、至少保证上面的代码能看懂。

  • 将系统 SCNetworkReachabilityFlags 转化成AF对外的网络状态枚举 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);
    // 是否可以自动联网 => 条件: 1.存在网络 2.可以自动尝试并完成连接
    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 {
        
        //WiFi
        status = AFNetworkReachabilityStatusReachableViaWiFi;
    }

    return status;
}

关于 SCNetworkReachabilityFlags 枚举

typedef CF_OPTIONS(uint32_t, SCNetworkReachabilityFlags) {
      // 可以通过瞬时连接(例如PPP---点对点通讯协议) 链接到给定的节点名和地址
    kSCNetworkReachabilityFlagsTransientConnection  = 1<<0,
        //能够连接网络
    kSCNetworkReachabilityFlagsReachable        = 1<<1, 
        //能够连接网络、但是首先得建立连接过程
    kSCNetworkReachabilityFlagsConnectionRequired   = 1<<2, = 1<<3,
    //当前网络配置可以请求到给定的节点名和地址,但必须先通过确认用户创建一个连接。
用户需要通过某些方式的介入来创建该连接,比如提供密码、认证口令等。一般的,只有在一个拨号通信的配置中,在自动连接过程中发生某些错误(比如没有拨号音,没有回应,无效的密码等),这种情况下,PPP通信将会终止连接直到用户介入。
    kSCNetworkReachabilityFlagsInterventionRequired = 1<<4,
    //当前网络配置可以请求到给定的节点名和地址,但必须先创建一个连接。该请求一经CFSocketStream程序接口请求就创建,其他的函数不会创建该连接
    kSCNetworkReachabilityFlagsConnectionOnDemand   = 1<<5, // __OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_3_0)
    //所请求的节点或地址为连接到当前系统的网络节点  
    kSCNetworkReachabilityFlagsIsLocalAddress   = 1<<16,
    //网络通信不会通过网关连接给定的节点名和地址,而是直接路由到系统中的一个接口上
    kSCNetworkReachabilityFlagsIsDirect     = 1<<17,
#if TARGET_OS_IPHONE
    //可通过蜂窝数据网络访问给定的节点或地址
    kSCNetworkReachabilityFlagsIsWWAN       = 1<<18,
#endif  // TARGET_OS_IPHONE

    kSCNetworkReachabilityFlagsConnectionAutomatic  = kSCNetworkReachabilityFlagsConnectionOnTraffic
};
  • 注册键值依赖

KVO的一个冷门方法

+ (NSSet *)keyPathsForValuesAffectingValueForKey:(NSString *)key {
    
    if ([key isEqualToString:@"reachable"] || [key isEqualToString:@"reachableViaWWAN"] || [key isEqualToString:@"reachableViaWiFi"]) {
        
        return [NSSet setWithObject:@"networkReachabilityStatus"];
    }

    return [super keyPathsForValuesAffectingValueForKey:key];
}

return 的 值被改变的时候、触发 key 的监听

也就是说当 networkReachabilityStatus 改变的时候、 reachable / reachableViaWWAN / reachableViaWiFi 的KVO监听都将被触发

API注释Demo

把注释的源码放在了github上、有兴趣可以自取。

GitHub

参考资料


分享给小伙伴们:
本文标签: AFNetworkingiOS

相关文章

发表评论愿您的每句评论,都能给大家的生活添色彩,带来共鸣,带来思索,带来快乐。

CopyRight © 2015-2016 QingPingShan.com , All Rights Reserved.

清屏网 版权所有 豫ICP备15026204号