美文网首页
AFNetworking之AFNetworkReachabili

AFNetworking之AFNetworkReachabili

作者: OneAlon | 来源:发表于2018-01-04 20:56 被阅读81次

    此文章对AFNetworkReachabilityManager的源码做一些阅读记录和个人的理解.

    AFNetworkReachabilityManager个人理解为是对SCNetworkReachabilityRef的相关封装实现的. 在讲解AFNetworkReachabilityManager之前, 需要先对SCNetworkReachabilityRef有一些了解, 附上SCNetworkReachabilityRef监测网络状态.

    AFNetworkReachabilityManager对网络的监测使用如下:

        AFNetworkReachabilityManager *networkReachManager = [AFNetworkReachabilityManager sharedManager];
        [networkReachManager setReachabilityStatusChangeBlock:^(AFNetworkReachabilityStatus status) {
            NSLog(@"%zd", status);
        }];
        // 开始监测网络
        [networkReachManager startMonitoring];
    

    当网络状态发生变化时就会执行block, 将网络状态AFNetworkReachabilityStatus返回给我们.

    AFNetworkReachabilityManager.h文件

    /**
     网络类型
    
     - AFNetworkReachabilityStatusUnknown: 未知网络
     - AFNetworkReachabilityStatusNotReachable: 网络不可达, 无网络
     - AFNetworkReachabilityStatusReachableViaWWAN: 手机网络
     - AFNetworkReachabilityStatusReachableViaWiFi: WiFi
     */
    typedef NS_ENUM(NSInteger, AFNetworkReachabilityStatus) {
        AFNetworkReachabilityStatusUnknown          = -1,
        AFNetworkReachabilityStatusNotReachable     = 0,
        AFNetworkReachabilityStatusReachableViaWWAN = 1,
        AFNetworkReachabilityStatusReachableViaWiFi = 2,
    };
    

    对外提供四种网络状态的枚举值.

    /**
     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.
     当前连接是否是WWAN
     */
    @property (readonly, nonatomic, assign, getter = isReachableViaWWAN) BOOL reachableViaWWAN;
    
    /**
     Whether or not the network is currently reachable via WiFi.
     当前连接是否是WiFi
     */
    @property (readonly, nonatomic, assign, getter = isReachableViaWiFi) BOOL reachableViaWiFi;
    

    在.h文件中对外提供了4个只读属性, 并且给出了对应的getter方法.

    + (instancetype)sharedManager;
    
    + (instancetype)manager;
    
    + (instancetype)managerForDomain:(NSString *)domain;
    
    + (instancetype)managerForAddress:(const void *)address;
    
    - (instancetype)initWithReachability:(SCNetworkReachabilityRef)reachability NS_DESIGNATED_INITIALIZER;
    
    - (nullable instancetype)init NS_UNAVAILABLE;
    

    提供几个初始化方法, 创建实例对象.

    /**
     Starts monitoring for changes in network reachability status.
      开始监听
     */
    - (void)startMonitoring;
    
    /**
     Stops monitoring for changes in network reachability status.
      结束监听
     */
    - (void)stopMonitoring;
    
    /**
     Returns a localized string representation of the current network reachability status.
     返回一个网络状态的字符串
     */
    - (NSString *)localizedNetworkReachabilityStatusString;
    
    /**
     Sets a callback to be executed when the network availability of the `baseURL` host changes.
    
     @param block A block object to be executed when the network availability of the `baseURL` host changes.. This block has no return value and takes a single argument which represents the various reachability states from the device to the `baseURL`.
     网络状态改变的回调
     监听网络状态的改变有两种方法:1.是实现这个block 2.是监听通知
     */
    - (void)setReachabilityStatusChangeBlock:(nullable void (^)(AFNetworkReachabilityStatus status))block;
    
    /**
     Posted when network reachability changes.
     This notification assigns no notification object. The `userInfo` dictionary contains an `NSNumber` object under the `AFNetworkingReachabilityNotificationStatusItem` key, representing the `AFNetworkReachabilityStatus` value for the current network reachability.
    
     @warning In order for network reachability to be monitored, include the `SystemConfiguration` framework in the active target's "Link Binary With Library" build phase, and add `#import <SystemConfiguration/SystemConfiguration.h>` to the header prefix of the project (`Prefix.pch`).
     网络状态改变时 发送的通知
     在userInfo下有以AFNetworkingReachabilityNotificationStatusItem为key的一个NSNumber类型的值, 这个值对应着AFNetworkReachabilityStatus枚举, 反应网络状态
     FOUNDATION_EXPORT主要用于定义常量
     */
    FOUNDATION_EXPORT NSString * const AFNetworkingReachabilityDidChangeNotification;
    FOUNDATION_EXPORT NSString * const AFNetworkingReachabilityNotificationStatusItem;
    
    /**
     Returns a localized string representation of an `AFNetworkReachabilityStatus` value.
    这个是定义的一个C语言函数, 返回本地化的status字符串
     */
    FOUNDATION_EXPORT NSString * AFStringFromNetworkReachabilityStatus(AFNetworkReachabilityStatus status);
    

    定义了两个通知的名称, 当网络状态改变时发出的通知, 接收的通知中会有一个userInfo, 可根据keyAFNetworkingReachabilityNotificationStatusItem取出通知的内容.
    我们可以借鉴AFN中通知的实现, 在开发中可以将通知的key定义到一个专门管理常量的文件中, 按照不同的模块进行划分.

    AFNetworkReachabilityManager.m文件

    // 一个静态字符串, 网络状态发生变化时发出的通知 对应.h
    NSString * const AFNetworkingReachabilityDidChangeNotification = @"com.alamofire.networking.reachability.change";
    // 网络状态发生变化时发出通知, 携带的数据
    NSString * const AFNetworkingReachabilityNotificationStatusItem = @"AFNetworkingReachabilityNotificationStatusItem";
    
    // 将枚举类型转换成字符串(这是对在.h中声明的实现)
    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);
        }
    }
    

    对在.h文件中定义的常量的赋值和函数的实现.

    // 定义block类型, 当网络状态改变时调用的block
    typedef void (^AFNetworkReachabilityStatusBlock)(AFNetworkReachabilityStatus status);
    
    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;
    }
    

    根据SCNetworkReachabilityFlags这个网络标记转换成AFN中的网络状态
    static修饰全局, 只能在当前文件使用
    在我们开发的过程中经常会使用私有方法, 将私有方法写成c语言函数的形式的好处(个人理解)(static void functionName(){}):
    1.在文件的最前边, 方便查找
    2.可以使用内联函数, 提高效率

    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];
        });
    }
    

    监听网络状态的改变有两种, 一种是实现setReachabilityStatusChangeBlock:中的block, 另一种是监听通知AFNetworkingReachabilityDidChangeNotification, 此方法将监听网络状态改变的两种方式封装到一个函数中, 在主队列中异步执行. 这种方法也是值得学习的地方.

    static void AFNetworkReachabilityCallback(SCNetworkReachabilityRef __unused target, SCNetworkReachabilityFlags flags, void *info) {
        AFPostReachabilityStatusChange(flags, (__bridge AFNetworkReachabilityStatusBlock)info);
    }
    

    这个函数直接调用上边的函数.

    static const void * AFNetworkReachabilityRetainCallback(const void *info) {
        return Block_copy(info);
    }
    
    static void AFNetworkReachabilityReleaseCallback(const void *info) {
        if (info) {
            Block_release(info);
        }
    }
    

    void *个人理解为OC中的id类型, 可以指向任何类型. block其实也是对象, 我们可以对其进行retain操作, 在block做为属性的时候我们通常用copy去修饰block, 将block拷贝到堆内存中.
    这两个block用于创建SCNetworkReachabilityContext结构体, 下边会讲到.

    // SCNetworkReachabilityRef 网络连接引用
    @property (readonly, nonatomic, assign) SCNetworkReachabilityRef networkReachability;
    // 网络状态, 枚举类型
    @property (readwrite, nonatomic, assign) AFNetworkReachabilityStatus networkReachabilityStatus;
    // 网络状态切换block
    @property (readwrite, nonatomic, copy) AFNetworkReachabilityStatusBlock networkReachabilityStatusBlock;
    

    .m文件中定义的三个属性.

    - (void)startMonitoring {
        [self stopMonitoring];
    
        // 句柄
        if (!self.networkReachability) {
            return;
        }
    
        // callback回调
        __weak __typeof(self)weakSelf = self;
        AFNetworkReachabilityStatusBlock callback = ^(AFNetworkReachabilityStatus status) {
            __strong __typeof(weakSelf)strongSelf = weakSelf;
    
            strongSelf.networkReachabilityStatus = status;
            if (strongSelf.networkReachabilityStatusBlock) {
                strongSelf.networkReachabilityStatusBlock(status);
            }
    
        };
    
        /**
         typedef struct {
         CFIndex        version;
         void *        __nullable info;// void * 相当于oc中的id类型, 可以指向任何类型的参数
         const void    * __nonnull (* __nullable retain)(const void *info);// 接收一个函数, 目的是对info做retain操作
         void        (* __nullable release)(const void *info);// 接收一个函数, 目的是对info做release操作
         CFStringRef    __nonnull (* __nullable copyDescription)(const void *info);// 接收一个函数, 根据info获取description字符串
         } SCNetworkReachabilityContext;
    
         context是一个结构体
         */
        SCNetworkReachabilityContext context = {0, (__bridge void *)callback, AFNetworkReachabilityRetainCallback, AFNetworkReachabilityReleaseCallback, NULL};
        // 根据上下文设置回调
        SCNetworkReachabilitySetCallback(self.networkReachability, AFNetworkReachabilityCallback, &context);
        // 加入线程池中 mainRunLoop commonModes
        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);
            }
        });
    }
    

    这个是主要讲解的方法, AFNetworkReachabilityManager监测网络状态的核心就在此方法.
    SCNetworkReachabilityContext是一个结构体(个人理解为结构体主要用于存储数据), void * __nullable info是指向需要执行的block的指针, 包含了用户指定的数据和用于SCNetworkReachabilitySetCallback方法的回调函数.
    SCNetworkReachabilitySetCallback设置回调, 可以看我的另一篇文章有简要介绍.
    SCNetworkReachabilityScheduleWithRunLoopnetworkReachability网络连接引用加入到运行循环中. 个人理解为加入运行循环以后会一直监测networkReachability网络状态, 如果网络状态有变化就会调用AFNetworkReachabilityCallback.
    在异步线程中发送一次网络状态, 调用SCNetworkReachabilityGetFlags获取网络状态, AFPostReachabilityStatusChange发送网路状态.

    - (void)stopMonitoring {
        if (!self.networkReachability) {
            return;
        }
    
        SCNetworkReachabilityUnscheduleFromRunLoop(self.networkReachability, CFRunLoopGetMain(), kCFRunLoopCommonModes);
    }
    

    从指定的运行循环和模式中移除网络连接引用的调度. 也就是说不再监听networkReachability的网络状态.

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

    键值依赖, 返回一个键集合, 这些属性的值会影响指定的key的值, 当集合中键的值发生变化时, 就会触发指定key的监听通知.
    networkReachabilityStatus的值发生变化时, 就会触发指定的key的键值监听方法.



    利用一天的时间整理了一下, 如果有错误的地方, 希望能够指出, 共同进步.

    相关文章

      网友评论

          本文标题:AFNetworking之AFNetworkReachabili

          本文链接:https://www.haomeiwen.com/subject/oewrnxtx.html