源码阅读:AFNetworking(五)——AFNetworkReachabilityManager

该文章阅读的AFNetworking的版本为3.2.0。

该类主要是用来检测网络环境的变化。

1.接口文件

1.1.枚举

typedef NS_ENUM(NSInteger, AFNetworkReachabilityStatus) {
    AFNetworkReachabilityStatusUnknown          = -1,
    AFNetworkReachabilityStatusNotReachable     = 0,
    AFNetworkReachabilityStatusReachableViaWWAN = 1,
    AFNetworkReachabilityStatusReachableViaWiFi = 2,
};
复制代码

这个枚举提供了可以监控的网络状态:

AFNetworkReachabilityStatusUnknown 网络状态未知 AFNetworkReachabilityStatusNotReachable 网络状态无连接 AFNetworkReachabilityStatusReachableViaWWAN 网络状态为蜂窝移动网络连接 AFNetworkReachabilityStatusReachableViaWiFi 网络状态为无线局域网连接

1.2.属性

/**
 当前网络连接状态
 */
@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;
复制代码

1.3.方法

/**
 获取单例对象
 */
+ (instancetype)sharedManager;

/**
 实例化默认socket地址的对象,并主动监视默认socket地址的状态
 */
+ (instancetype)manager;

/**
 实例化指定域的对象,并主动监视指定域的状态
 */
+ (instancetype)managerForDomain:(NSString *)domain;

/**
 实例化指定socket地址的对象,并主动监视指定socket地址的状态
 */
+ (instancetype)managerForAddress:(const void *)address;

/**
 以指定SCNetworkReachabilityRef对象进行初始化,并主动监视指定SCNetworkReachabilityRef对象的状态
 */
- (instancetype)initWithReachability:(SCNetworkReachabilityRef)reachability NS_DESIGNATED_INITIALIZER;

/**
 初始化方法,这个方法被手动禁止了,如果强制调用这个方法就会报错
 */
- (nullable instancetype)init NS_UNAVAILABLE;

/**
 开始监控网络状态
 */
- (void)startMonitoring;

/**
 停止监控网络状态
 */
- (void)stopMonitoring;

/**
 获得网络状态的本地文字描述
 */
- (NSString *)localizedNetworkReachabilityStatusString;

/**
 当网络状态发生变化时,会回调block
 */
- (void)setReachabilityStatusChangeBlock:(nullable void (^)(AFNetworkReachabilityStatus status))block;
复制代码

1.4.全局静态常量

FOUNDATION_EXPORT NSString * const AFNetworkingReachabilityDidChangeNotification;
FOUNDATION_EXPORT NSString * const AFNetworkingReachabilityNotificationStatusItem;
复制代码

这两个常量是用在通过通知来监听网络状态变化。当需要用通知来监听网络状态变化时,先监听AFNetworkingReachabilityDidChangeNotification通知,然后在通知调用的方法中获取传递过来参数中的属性userInfo,这个属性是NSDictionary类型的,通过key值AFNetworkingReachabilityNotificationStatusItem就可以获得当前的网络状态。

1.5.全局方法

FOUNDATION_EXPORT NSString * AFStringFromNetworkReachabilityStatus(AFNetworkReachabilityStatus status);
复制代码

这个方法的作用是将传入的网络状态枚举值转成本地文字描述。

2.实现文件

2.1.全局静态常量

NSString * const AFNetworkingReachabilityDidChangeNotification = @"com.alamofire.networking.reachability.change";
NSString * const AFNetworkingReachabilityNotificationStatusItem = @"AFNetworkingReachabilityNotificationStatusItem";
复制代码

.h文件中全局静态常量赋值

2.2.别名

typedef void (^AFNetworkReachabilityStatusBlock)(AFNetworkReachabilityStatus status);
复制代码

为参数为AFNetworkReachabilityStatus类型无返回值的block起名为AFNetworkReachabilityStatusBlock

2.3.私有方法

/**
 这个方法就是.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);
    }
}

/**
 该方法是将SCNetworkReachabilityFlags类型的枚举转换成AFNetworking自定义的网络状态枚举
 */
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;
}

/**
 为了保证网络状态变化时block回调和发送通知的统一性,将两者在主线程统一调用
 */
static void AFPostReachabilityStatusChange(SCNetworkReachabilityFlags flags, AFNetworkReachabilityStatusBlock block) {
    // 将flags转成自定义网络状态枚举值
    AFNetworkReachabilityStatus status = AFNetworkReachabilityStatusForFlags(flags);
    // 主队列异步
    dispatch_async(dispatch_get_main_queue(), ^{
        // 回调block
        if (block) {
            block(status);
        }
        // 发送通知
        NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter];
        NSDictionary *userInfo = @{ AFNetworkingReachabilityNotificationStatusItem: @(status) };
        [notificationCenter postNotificationName:AFNetworkingReachabilityDidChangeNotification object:nil userInfo:userInfo];
    });
}

/**
 将上面的方法封装,作为参数传入网络状态监控回调方法SCNetworkReachabilitySetCallback中
 */
static void AFNetworkReachabilityCallback(SCNetworkReachabilityRef __unused target, SCNetworkReachabilityFlags flags, void *info) {
    AFPostReachabilityStatusChange(flags, (__bridge AFNetworkReachabilityStatusBlock)info);
}

/**
 将block复制到堆中,并封装成方法作为参数构建SCNetworkReachabilityContext结构体
 */
static const void * AFNetworkReachabilityRetainCallback(const void *info) {
    return Block_copy(info);
}

/**
 释放复制到堆中的block,并封装成方法作为参数构建SCNetworkReachabilityContext结构体
 */
static void AFNetworkReachabilityReleaseCallback(const void *info) {
    if (info) {
        Block_release(info);
    }
}
复制代码

2.4.类扩展

/**
 保存传入的SCNetworkReachabilityRef对象
 */
@property (readonly, nonatomic, assign) SCNetworkReachabilityRef networkReachability;

/**
 保存当前网络状态的枚举值
 */
@property (readwrite, nonatomic, assign) AFNetworkReachabilityStatus networkReachabilityStatus;

/**
 保存传入的用于回调的block
 */
@property (readwrite, nonatomic, copy) AFNetworkReachabilityStatusBlock networkReachabilityStatusBlock;
复制代码

2.5.方法实现

+ (instancetype)sharedManager {
    // 调用以默认socket地址实例化对象的方法,生成单例对象
    static AFNetworkReachabilityManager *_sharedManager = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        _sharedManager = [self manager];
    });

    return _sharedManager;
}

+ (instancetype)managerForDomain:(NSString *)domain {
    // 利用传入的域生成SCNetworkReachabilityRef对象
    SCNetworkReachabilityRef reachability = SCNetworkReachabilityCreateWithName(kCFAllocatorDefault, [domain UTF8String]);
    
    // 利用SCNetworkReachabilityRef对象实例化AFNetworkReachabilityManager对象
    AFNetworkReachabilityManager *manager = [[self alloc] initWithReachability:reachability];
    
    // 释放SCNetworkReachabilityRef对象
    CFRelease(reachability);

    return manager;
}

+ (instancetype)managerForAddress:(const void *)address {
    // 利用传入的地址象实例化AFNetworkReachabilityManager对象
    SCNetworkReachabilityRef reachability = SCNetworkReachabilityCreateWithAddress(kCFAllocatorDefault, (const struct sockaddr *)address);
    
    // 利用SCNetworkReachabilityRef对象实例化AFNetworkReachabilityManager对象
    AFNetworkReachabilityManager *manager = [[self alloc] initWithReachability:reachability];

    // 释放SCNetworkReachabilityRef对象
    CFRelease(reachability);
    
    return manager;
}

+ (instancetype)manager
{
    // 根据版本号适配IPv6和IPv4,获得默认地址
#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];
}

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

    // 保存或初始化属性
    _networkReachability = CFRetain(reachability);
    self.networkReachabilityStatus = AFNetworkReachabilityStatusUnknown;

    return self;
}

- (instancetype)init NS_UNAVAILABLE
{
    return nil;
}

- (void)dealloc {
    // 停止监控
    [self stopMonitoring];
    
    // 释放SCNetworkReachabilityRef对象
    if (_networkReachability != NULL) {
        CFRelease(_networkReachability);
    }
}

- (BOOL)isReachable {
    return [self isReachableViaWWAN] || [self isReachableViaWiFi];
}

- (BOOL)isReachableViaWWAN {
    return self.networkReachabilityStatus == AFNetworkReachabilityStatusReachableViaWWAN;
}

- (BOOL)isReachableViaWiFi {
    return self.networkReachabilityStatus == AFNetworkReachabilityStatusReachableViaWiFi;
}

- (void)startMonitoring {
    // 先停止之前的网络监控
    [self stopMonitoring];

    // 如果没有传入或者生成SCNetworkReachabilityRef对象就直接返回
    if (!self.networkReachability) {
        return;
    }

    // 构造网络监控所需的参数
    __weak __typeof(self)weakSelf = self;
    AFNetworkReachabilityStatusBlock callback = ^(AFNetworkReachabilityStatus status) {
        __strong __typeof(weakSelf)strongSelf = weakSelf;
        
        // 回调网络状态
        strongSelf.networkReachabilityStatus = status;
        if (strongSelf.networkReachabilityStatusBlock) {
            strongSelf.networkReachabilityStatusBlock(status);
        }

    };

    // 生成网络监控上下文
    SCNetworkReachabilityContext context = {0, (__bridge void *)callback, AFNetworkReachabilityRetainCallback, AFNetworkReachabilityReleaseCallback, NULL};
    // 设置网络监控回调
    SCNetworkReachabilitySetCallback(self.networkReachability, AFNetworkReachabilityCallback, &context);
    // 放到运行循环中
    SCNetworkReachabilityScheduleWithRunLoop(self.networkReachability, CFRunLoopGetMain(), kCFRunLoopCommonModes);

    // 在全局并发队列中异步监听SCNetworkReachabilityRef对象的网络状态,如果发生变化则进行回调和发送通知
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0),^{
        SCNetworkReachabilityFlags flags;
        if (SCNetworkReachabilityGetFlags(self.networkReachability, &flags)) {
            AFPostReachabilityStatusChange(flags, callback);
        }
    });
}

- (void)stopMonitoring {
    // 如果没有传入或者生成SCNetworkReachabilityRef对象就直接返回
    if (!self.networkReachability) {
        return;
    }

    // 将SCNetworkReachabilityRef对象从运行循环中移除
    SCNetworkReachabilityUnscheduleFromRunLoop(self.networkReachability, CFRunLoopGetMain(), kCFRunLoopCommonModes);
}

- (NSString *)localizedNetworkReachabilityStatusString {
    return AFStringFromNetworkReachabilityStatus(self.networkReachabilityStatus);
}

- (void)setReachabilityStatusChangeBlock:(void (^)(AFNetworkReachabilityStatus status))block {
    self.networkReachabilityStatusBlock = block;
}

复制代码

这其中有一点是在使用GCD时传入了参数DISPATCH_QUEUE_PRIORITY_BACKGROUND,这个不常用的参数有着和AFURLRequestSerialization类中的throttleBandwidthWithPacketSize:delay:方法共同的知识点,感兴趣的话可以看这篇文章

2.6.KVO

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

    return [super keyPathsForValuesAffectingValueForKey:key];
}
复制代码

添加依赖键,使reachablereachableViaWWANreachableViaWiFi属性依赖于networkReachabilityStatus属性,当networkReachabilityStatus属性值发生变化时,reachablereachableViaWWANreachableViaWiFi属性的观察者也能得到通知。

源码阅读系列:AFNetworking

源码阅读:AFNetworking(一)——从使用入手

源码阅读:AFNetworking(二)——AFURLRequestSerialization

源码阅读:AFNetworking(三)——AFURLResponseSerialization

源码阅读:AFNetworking(四)——AFSecurityPolicy

源码阅读:AFNetworking(五)——AFNetworkReachabilityManager

源码阅读:AFNetworking(六)——AFURLSessionManager

源码阅读:AFNetworking(七)——AFHTTPSessionManager

源码阅读:AFNetworking(八)——AFAutoPurgingImageCache

源码阅读:AFNetworking(九)——AFImageDownloader

源码阅读:AFNetworking(十)——AFNetworkActivityIndicatorManager

源码阅读:AFNetworking(十一)——UIActivityIndicatorView+AFNetworking

源码阅读:AFNetworking(十二)——UIButton+AFNetworking

源码阅读:AFNetworking(十三)——UIImageView+AFNetworking

源码阅读:AFNetworking(十四)——UIProgressView+AFNetworking

源码阅读:AFNetworking(十五)——UIRefreshControl+AFNetworking

源码阅读:AFNetworking(十六)——UIWebView+AFNetworking

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值