AFNetworking 3.0 源码阅读笔记(六)

原文:http://itangqi.me/2016/05/17/the-notes-of-learning-afnetworking-six/

前言

AFNetworkReachabilityManager 是对 SystemConfiguration 模块的封装,苹果的文档中也有一个类似的项目 Reachability 这里对网络状态的监控跟苹果官方的实现几乎是完全相同的。

同样在 GitHub 上有一个类似的项目叫做 Reachability 不过这个项目由于命名的原因可能会在审核时被拒绝

无论是 AFNetworkReachabilityManager,苹果官方的项目或者说 GitHub 上的 Reachability,它们的实现都是类似的,而在这里我们会以 AFNetworking 中的 AFNetworkReachabilityManager 为例来说明在 iOS 开发中,我们是怎样监控网络状态的。


AFNetworkReachabilityManager 的使用和实现

AFNetworkReachabilityManager 的使用还是非常简单的,只需要三个步骤,就基本可以完成对网络状态的监控:

  1. 初始化 AFNetworkReachabilityManager
  2. 调用 startMonitoring 方法开始对网络状态进行监控
  3. 设置 networkReachabilityStatusBlock 在每次网络状态改变时, 调用这个 block

初始化 AFNetworkReachabilityManager

在初始化方法中,使用 SCNetworkReachabilityCreateWithAddress 或者 SCNetworkReachabilityCreateWithName 生成一个 SCNetworkReachabilityRef 的引用:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
+ (instancetype)managerForDomain:(NSString *)domain {
    SCNetworkReachabilityRef reachability = SCNetworkReachabilityCreateWithName(kCFAllocatorDefault, [domain UTF8String]);

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

    return manager;
}

+ (instancetype)managerForAddress:(const void *)address {
    SCNetworkReachabilityRef reachability = SCNetworkReachabilityCreateWithAddress(kCFAllocatorDefault, (const struct sockaddr *)address);
    AFNetworkReachabilityManager *manager = [[self alloc] initWithReachability:reachability];

    return manager;
}
  1. 这两个方法会通过一个域名或者一个 sockaddr_in 的指针生成一个 SCNetworkReachabilityRef
  2. 调用 - [AFNetworkReachabilityManager initWithReachability:] 将生成的 SCNetworkReachabilityRef 引用传给 networkReachability
  3. 设置一个默认的 networkReachabilityStatus
1
2
3
4
5
6
7
8
9
10
11
- (instancetype)initWithReachability:(SCNetworkReachabilityRef)reachability {
    self = [super init];
    if (!self) {
        return nil;
    }

    self.networkReachability = CFBridgingRelease(reachability);
    self.networkReachabilityStatus = AFNetworkReachabilityStatusUnknown;

    return self;
}

当调用 CFBridgingRelease(reachability) 后,会把 reachability 桥接成一个 NSObject 对象赋值给 self.networkReachability,然后释放原来的 CoreFoundation 对象。

监控网络状态

在初始化 AFNetworkReachabilityManager 后,会调用 startMonitoring 方法开始监控网络状态:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
- (void)startMonitoring {
    // 1. 先调用 `- stopMonitoring` 方法,如果之前设置过对网络状态的监听,使用 `SCNetworkReachabilityUnscheduleFromRunLoop` 方法取消之前在 Main Runloop 中的监听
    [self stopMonitoring];

    if (!self.networkReachability) {
        return;
    }
    
    __weak __typeof(self)weakSelf = self;
    // 2. 创建一个在每次网络状态改变时的回调
    AFNetworkReachabilityStatusBlock callback = ^(AFNetworkReachabilityStatus status) {
        __strong __typeof(weakSelf)strongSelf = weakSelf;

        strongSelf.networkReachabilityStatus = status;
        if (strongSelf.networkReachabilityStatusBlock) {
            strongSelf.networkReachabilityStatusBlock(status);
        }

    };

    id networkReachability = self.networkReachability;
    // 3. 创建一个 `SCNetworkReachabilityContext`,其中的 `callback` 就是上一步中的创建的 block 对象
    SCNetworkReachabilityContext context = {0, (__bridge void *)callback, AFNetworkReachabilityRetainCallback, AFNetworkReachabilityReleaseCallback, NULL};
    // 4. 当目标的网络状态改变时,会调用传入的回调
    SCNetworkReachabilitySetCallback((__bridge SCNetworkReachabilityRef)networkReachability, AFNetworkReachabilityCallback, &context);
    // 5. 在 Main Runloop 中对应的模式开始监控网络状态
    SCNetworkReachabilityScheduleWithRunLoop((__bridge SCNetworkReachabilityRef)networkReachability, CFRunLoopGetMain(), kCFRunLoopCommonModes);
    // 6. 获取当前的网络状态,调用 callback
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0),^{
        SCNetworkReachabilityFlags flags;
        if (SCNetworkReachabilityGetFlags((__bridge SCNetworkReachabilityRef)networkReachability, &flags)) {
            AFPostReachabilityStatusChange(flags, callback);
        }
    });
}

1. 关于 AFNetworkReachabilityStatus

1
2
3
4
5
6
typedef NS_ENUM(NSInteger, AFNetworkReachabilityStatus) {
    AFNetworkReachabilityStatusUnknown          = -1,
    AFNetworkReachabilityStatusNotReachable     = 0,
    AFNetworkReachabilityStatusReachableViaWWAN = 1,
    AFNetworkReachabilityStatusReachableViaWiFi = 2,
};

2. 关于 SCNetworkReachabilityContext

1
2
3
4
5
6
7
8
9
10
11
12
typedef struct {
    // 创建一个 SCNetworkReachabilityContext 结构体时,需要调用 SCDynamicStore 的创建函数,而此创建函数会根据 version 来创建出不同的结构体,SCNetworkReachabilityContext 对应的 version 是 0
    CFIndex		version;
    // 下面两个block(release 和 retain)的参数就是 info,此处表示的是网络状态处理的回调函数
    void *		__nullable info;
    // 该 retain block 用于对 info 进行 retain,下面那个 AFNetworkReachabilityRetainCallback 核心就是调用了 Block_copy(用于 retain 一个 block 函数,即在堆空间新建或直接引用一个 block 拷贝)
    const void	* __nonnull (* __nullable retain)(const void *info);
    // 该 release block 用于对 info 进行 release,下面那个 AFNetworkReachabilityReleaseCallback 核心就是调用了 Block_release(用于 release 一个 block 函数,即将 block 从堆空间移除或移除相应引用)
    void		(* __nullable release)(const void *info);
    // 提供 info 的 description,此处调用为 NULL
    CFStringRef	__nonnull (* __nullable copyDescription)(const void *info);
} SCNetworkReachabilityContext;

在下一节中会介绍上面所提到的一些 C 函数以及各种回调。

设置 networkReachabilityStatusBlock 以及回调

在 Main Runloop 中对网络状态进行监控之后,在每次网络状态改变,就会调用 AFNetworkReachabilityCallback 函数:

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

这里会从 info 中取出之前存在 context 中的 AFNetworkReachabilityStatusBlock

1
2
3
4
5
6
7
8
9
10
__weak __typeof(self)weakSelf = self;
AFNetworkReachabilityStatusBlock callback = ^(AFNetworkReachabilityStatus status) {
    __strong __typeof(weakSelf)strongSelf = weakSelf;

    strongSelf.networkReachabilityStatus = status;
    if (strongSelf.networkReachabilityStatusBlock) {
        strongSelf.networkReachabilityStatusBlock(status);
    }

};

取出这个 block 之后,传入 AFPostReachabilityStatusChange 函数:

1
2
3
4
5
6
7
8
9
10
11
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];
    });
}
  1. 调用 AFNetworkReachabilityStatusForFlags 获取当前的网络可达性状态
  2. 在主线程中异步执行上面传入的 callback block(设置 self 的网络状态,调用 networkReachabilityStatusBlock
  3. 发送 AFNetworkingReachabilityDidChangeNotification 通知.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
static AFNetworkReachabilityStatus AFNetworkReachabilityStatusForFlags(SCNetworkReachabilityFlags flags) {
    // 该网络地址可达
    BOOL isReachable = ((flags & kSCNetworkReachabilityFlagsReachable) != 0);
    // 该网络地址虽然可达,但是需要先建立一个 connection
    BOOL needsConnection = ((flags & kSCNetworkReachabilityFlagsConnectionRequired) != 0);
    // 该网络虽然也需要先建立一个 connection,但是它是可以自动去 connect 的
    BOOL canConnectionAutomatically = (((flags & kSCNetworkReachabilityFlagsConnectionOnDemand ) != 0) || ((flags & kSCNetworkReachabilityFlagsConnectionOnTraffic) != 0));
    // 不需要用户交互,就可以 connect 上(用户交互一般指的是提供网络的账户和密码)
    BOOL canConnectWithoutUserInteraction = (canConnectionAutomatically && (flags & kSCNetworkReachabilityFlagsInterventionRequired) == 0);
    // 如果 isReachable==YES,那么就需要判断是不是得先建立一个 connection,如果需要,那就认为不可达,或者虽然需要先建立一个 connection,但是不需要用户交互,那么认为也是可达的
    BOOL isNetworkReachable = (isReachable && (!needsConnection || canConnectWithoutUserInteraction));
    
    //  AFNetworkReachabilityStatus 就四种状态 Unknown、NotReachable、ReachableViaWWAN、ReachableViaWiFi,这四种状态字面意思很好理解,这里就不赘述了
    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;
}

因为 flags 是一个 SCNetworkReachabilityFlags,它的不同位代表了不同的网络可达性状态,通过 flags 的位操作,获取当前的状态信息 AFNetworkReachabilityStatus

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
typedef CF_OPTIONS(uint32_t, SCNetworkReachabilityFlags) {
    kSCNetworkReachabilityFlagsTransientConnection = 1<<0,
    kSCNetworkReachabilityFlagsReachable = 1<<1,
    kSCNetworkReachabilityFlagsConnectionRequired = 1<<2,
    kSCNetworkReachabilityFlagsConnectionOnTraffic = 1<<3,
    kSCNetworkReachabilityFlagsInterventionRequired = 1<<4,
    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
};

这里就是在 SystemConfiguration 中定义的全部的网络状态的标志位。


与 AFNetworking 协作

其实 AFNetworkReachabilityManagerAFNetworking 整个框架并没有太多的耦合。正相反,它在整个框架中作为一个即插即用的类使用,每一个 AFURLSessionManager 都会持有一个 AFNetworkReachabilityManager 的实例。

1
self.reachabilityManager = [AFNetworkReachabilityManager sharedManager];

这是整个框架中除了 AFNetworkReachabilityManager.h/m 文件,唯一一个引用到这个类的地方。


总结

  • AFNetworkReachabilityManager 实际上只是对底层 SystemConfiguration 库中的 C 函数封装的类(类似于 GCD),它为我们隐藏了 C 语言的实现,提供了统一且简洁的 Objective-C 语言接口
  • 它是 AFNetworking 中一个即插即用的模块

参考

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值