AFNetworking之AFNetworkReachabilityManager深入学习

此文章对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 ` 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之AFNetworkReachabilityManager深入学习)