AFNetworkReachabilityManager 阅读笔记(项目问题总结)

AFNetworkReachabilityManager 阅读笔记

AFNetworkReachabilityManager 是 AFNetworking 中用来监听网络可达性的组件,(有没有网络,什么网络)之类的。

ps:不能根据这个状态来阻止用户发送网络请求。

我们项目中,对网络状态变化做了一层封装,如下:

@interface PTVNetworkStatus()
@property(strong,nonatomic)AFHTTPSessionManager* session;
@end


@implementation PTVNetworkStatus


-(id)init
{
    if (self = [super init]) {
        _session = [[AFHTTPSessionManager alloc] initWithBaseURL:[NSURL URLWithString:@"https://api.m.panda.tv"]];
        _session.securityPolicy = [AFSecurityPolicy policyWithPinningMode:AFSSLPinningModeNone];
        
        [_session.reachabilityManager setReachabilityStatusChangeBlock:^(AFNetworkReachabilityStatus status) {
            switch (status) {
                case AFNetworkReachabilityStatusReachableViaWWAN:
                    NSLog(@"-------AFNetworkReachabilityStatusReachableViaWWAN------");
                    break;
                    
                case AFNetworkReachabilityStatusReachableViaWiFi:
                    NSLog(@"-------AFNetworkReachabilityStatusReachableViaWiFi------");
                    break;
                case AFNetworkReachabilityStatusNotReachable:
                    NSLog(@"-------AFNetworkReachabilityStatusNotReachable------");
                    break;
                default:
                    break;
            }
            if (_status != status) {
                _status = status;
                [[NSNotificationCenter defaultCenter] postNotificationName:notificationName object:nil];
            }
        }];
        [_session.reachabilityManager startMonitoring];
        
    }
    return self;
}

我觉得这个包装有问题。所以决定深入看一下代码,

_session = [[AFHTTPSessionManager alloc] initWithBaseURL:[NSURL URLWithString:@"https://xxxxx.xxxxx"]];
        _session.securityPolicy = [AFSecurityPolicy policyWithPinningMode:AFSSLPinningModeNone];
        

这里initWithBaseUrl 的参数 https://xxxxx.xxxxx 对网络状态监听并没有影响。

纠错

我们看看 AFHTTPSessionManager 的初始化函数:

在 AFURLSessionManager 的 init 函数中有默认的 reachabilityManager 实现。

self.reachabilityManager = [AFNetworkReachabilityManager sharedManager];

默认的实现是不会用到刚才传入的参数的。

+ (instancetype)sharedManager {
    static AFNetworkReachabilityManager *_sharedManager = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        struct sockaddr_in address;
        bzero(&address, sizeof(address));
        address.sin_len = sizeof(address);
        address.sin_family = AF_INET;

        _sharedManager = [self managerForAddress:&address];
    });

    return _sharedManager;
}

所以项目中的正确用法应该是使用 managerForDomain 函数进行初始化 AFNetworkReachabilityManager 对象。然后使用 setReachabilityStatusChangeBlock 监听网络状态。。。

比如:

AFNetworkReachabilityManager *manager = [AFNetworkReachabilityManager managerForDomain:@"www.google.com"];
[manager setReachabilityStatusChangeBlock:^(AFNetworkReachabilityStatus status){
    switch (status) {
        case AFNetworkReachabilityStatusReachableViaWWAN:
        case AFNetworkReachabilityStatusReachableViaWiFi:
        case AFNetworkReachabilityStatusNotReachable:
            NSLog(@"Never called");
            break;
        default:
            NSLog(@"Never called");
        break;
    }
}];
[manager startMonitoring];

如果项目要包装 AFNetworkReachabilityManager 也不建议在 block 中直接发送通知,因为都在同一个类,如果通知忘记清空,倒是还有可能导致崩溃。

建议使用target/action 的方式,包装!一个列表,足以维护某个类需要的网络请求。

AFNetworkReachabilityManager 实现原理。

主要是基于 SCNetworkReachabilityRef 对网络状态进行监听的,系统本身已经支持监听网络状态,只是C 语言的形式,加上函数指针等等,对iOS 开发者,并不是很友好使用起来。

所以弄懂 AFNetworkReachabilityManager 只要点击看看 SCNetworkReachabilityRef 的相关函数以及文档就好。。

+ (instancetype)managerForDomain:(NSString *)domain {
    SCNetworkReachabilityRef reachability = SCNetworkReachabilityCreateWithName(kCFAllocatorDefault, [domain UTF8String]);

    AFNetworkReachabilityManager *manager = [[self alloc] initWithReachability:reachability];
    manager.networkReachabilityAssociation = AFNetworkReachabilityForName;

    return manager;
}

上面的函数式依据domain 创建一个 监听网络的对象。首先先创建一个 SCNetworkReachabilityRef 引用。

SCNetworkReachabilityRef的官方说明如下:

    The SCNetworkReachability API allows an application to
        determine the status of a system's current network
        configuration and the reachability of a target host.
        In addition, reachability can be monitored with notifications
        that are sent when the status has changed.

重点在于 函数 startMonitoring

- (void)startMonitoring {
    //先停止当前的监听
    [self stopMonitoring];
    ///如果没有 SCNetworkReachabilityRef 不能监听
    if (!self.networkReachability) {
        return;
    }
///创建网络变化回调的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};
    ///设置网络变化的回调
    SCNetworkReachabilitySetCallback((__bridge SCNetworkReachabilityRef)networkReachability, AFNetworkReachabilityCallback, &context);
    ///指定网络监听的runloop
    SCNetworkReachabilityScheduleWithRunLoop((__bridge SCNetworkReachabilityRef)networkReachability, CFRunLoopGetMain(), kCFRunLoopCommonModes);

    switch (self.networkReachabilityAssociation) {
        case AFNetworkReachabilityForName:
            break;
        case AFNetworkReachabilityForAddress:
        case AFNetworkReachabilityForAddressPair:
        default: {
            dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0),^{
                SCNetworkReachabilityFlags flags;
                SCNetworkReachabilityGetFlags((__bridge SCNetworkReachabilityRef)networkReachability, &flags);
                AFNetworkReachabilityStatus status = AFNetworkReachabilityStatusForFlags(flags);
                dispatch_async(dispatch_get_main_queue(), ^{
                    callback(status);

                    NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter];
                    [notificationCenter postNotificationName:AFNetworkingReachabilityDidChangeNotification object:nil userInfo:@{ AFNetworkingReachabilityNotificationStatusItem: @(status) }];


                });
            });
        }
            break;
    }
}

开始监听函数的实现过程。

停止监听的过程如下:

- (void)stopMonitoring {
    if (!self.networkReachability) {
        return;
    }

    SCNetworkReachabilityUnscheduleFromRunLoop((__bridge SCNetworkReachabilityRef)self.networkReachability, CFRunLoopGetMain(), kCFRunLoopCommonModes);
}

直接从runloop中移除当前的网络监听对象、

小结

使用第三方功能的时候,一定要对源码有足够的了解才行。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容