AFNetworking3.0源码解读(一)之 AFNetworkReachabilityManager

转载链接博主博客地址马在路上

AFNetworking 3.0 源码解读(二)之 AFSecurityPolicy

AFNetworking 3.0 源码解读(三)之 AFURLRequestSerialization

AFNetworking 3.0 源码解读(四)之 AFURLResponseSerialization

AFNetworking 3.0 源码解读(五)之 AFURLSessionManager

AFNetworking 3.0 源码解读(六)之 AFHTTPSessionManager

AFNetworking 3.0 源码解读(七)之 AFAutoPurgingImageCache

AFNetworking 3.0 源码解读(八)之 AFImageDownloader

AFNetworking 3.0 源码解读(九)之 AFNetworkActivityIndicatorManager

AFNetworking 3.0 源码解读(十)之 UIActivityIndicatorView/UIRefreshControl/UIImageView + AFNetworking

AFNetworking 3.0 源码解读(十一)之 UIButton/UIProgressView/UIWebView + AFNetworking

AFNetworking 3.0 源码解读 总结(干货)(上)

AFNetworking 3.0 源码解读 总结(干货)(下)

博主看了一下这位名叫马在路上博主的文章,深感解读的很好,故粘贴出来供大家参考。

做ios开发,AFNetworking 这个网络框架肯定都非常熟悉,也许我们平时只使用了它的部分功能,而且我们对它的实现原理并不是很清楚,就好像总是有一团迷雾在眼前一样。

接下来我们就非常详细的来读一读这个框架的代码,我们的目标就是理解了它的思想之后,能够明白我们的请求是如何实现的,我们的代码哪里还需要进行改进,如果能够更进一步,我们能够总结出一套适合大部分应用的网络架构思想。

能够让一些人从中受益。

我们先来看看整个框架的文件系统,我们先不对每个文件的作用进行说明,在整个源码解读最后的一篇中我们会对整个框架进行总结。会有一张清晰的图表来说明这个问题。

我们在看一个框架的时候呢,可以这样先看,先看每个文件的头文件,也就是.h文件

可以看到,有的头文件是包含了别的头文件的,在不考虑系统的头文件的情况下,我们能够发现一些比较独立的类,从上图中,我们可以看出

比较独立的类有:

1.AFURLResponseSerialization.h

2.AFNetworkReachabilityManager.h

3.AFURLRequestSerialization.h

4.AFSecurityPolicy.h

本篇就介绍AFNetworkReachabilityManager.h的内容,这个是用来监控网络环境变化的类。

 

#import <SystemConfiguration/SystemConfiguration.h>

通过导入了这个头文件,我们得知:网络监控的实现是依赖SystemConfiguration这个api的。说明这个api能够提供这样的功能,至少让我们明白了我们平时都会导入它的一个用途。

这是一个枚举封装,还是遵循一个使用枚举的原则,当满足一个有限的并具有统一主题的集合的时候,我们就考虑枚举。在这里作者是枚举了4种类型。这几种类型能够满足我们开发中大部分的功能,如果不满足,可以自行进行扩展。

NS_ASSUME_NONNULL_BEGIN
NS_ASSUME_NONNULL_END

这个是为了swift的可选类型配添加的,在这两个终点的内容的参数默认都是nonnull的。

这段文字是对这个类的说明。我们估且不去管它说了什么,在看看苹果官方的

*** 这样的内容会出现在一个属性或者方法的上方,目的是对其内容的解释。我看到这里就想到了我们平时的开发,我们能够把每段代码都当成是api的开发,也把注释写的详细一点。曾经看过两种不同的说辞,一种是说把代码注释尽量少些,要求代码简介可读性强。另一种是说注释要详细,着重考虑他人读代码的感受。个人感觉还是写详细一点比较好,因为可能过一段时间之后,自己再去看自己当时写的代码可能就不记得了。很有可能在写这些繁琐的注释的过程中,能够想到些什么,比如如何合并掉一些没必要的方法等等。

本类提供了四个只读的属性来让我们获取我们需要的内容

1. 网络状态

2. 是否是可达的

3. 当前连接是否是WWAN

4. 当前连接是够是WiFi

四个属性均为只读属性,只给了用户访问权,注意BOOL属性一般是要写getter方法的。

作者使用了这个来分隔同一类中不同功能模块。这个算是个人习惯问题吧。举个平时开发的例子,在.m文件中我个人使用#pragma mark 分隔不同功能。

提供了5中初始化方法,能够满足大部分的需求。

SCNetworkReachabilityRef 这个很重要,这个类的就是基于它开发的。

+ (instancetype)managerForDomain:(NSString *)domain; 监听制定domain的网络状态。

+ (instancetype)managerForAddress:(constvoid *)address; 监听某个socket地址的网络状态,socket通信请看这篇文章:socket通信

 

打开和关闭监听的方法。

返回一个网络状态的本地语言的字符串。往往我们可以根据这个字符串来告诉用户,当前网络发生了什么,当然,也可以根据状态自定义提示文字。

设置网络转态改变的回调,监听网络改变的回调有两种方式:

1.使用上边的这个方法。

2.监听AFNetworkingReachabilityDidChangeNotification通知。

 

这个是与网络状态变化相关的通知。接受的通知中会有一个userinfo 是一个NSDictionary 其中key就是

AFNetworkingReachabilityNotificationStatusItem 

*** 这简单的两行代码能够告诉我们的是,我们平时的开发中 但凡设计到发通知的功能,我们应该把通知的字符串封装到一个专有的文件中,同时在文件内部按不同模块进行区分,当然必要的注释也很有必要。

ps: FOUNDATION_EXPORT 和#define 都能定义常量。FOUNDATION_EXPORT 能够使用==进行判断,效率略高。而且能够隐藏定义细节(就是实现部分不在.中)

 

对函数:根据状态获取字符串  声明。

好了,这个类的.h文件我们已经非常相信的进行解读了,我们并不是大概的说了下他提供的功能,而是通过读每行代码,我们能联想到什么,什么东西能帮助我们更好的编程。

我们接着看 AFNetworkReachabilityManager.m 的内容

 

这几个头文件是系统库,是为了后边的 sockaddr_in6 / sockaddr_in 准备的,不熟悉的可以看这篇文章 socket通信

 

这几个就没什么好说的了,我们接着看

这个方法是对.h 中最后一个方法的实现。指的我们注意的是NSLocalizedStringFromTable这个宏。为什么要注意它呢?

这就涉及到本地国际化的问题。所谓的国际化就是让你的app能够根据不同的语言显示相对应的语言。

*** 但这并不简单,没有经验的开发人员,一开始可能不会做这样的设置,如果日后需要国际话了,在做就很麻烦了。所以说在开中,但凡使用到字符串的地方都要考虑语言的不同。不同的语言下,一个意思的表达所使用的字符串长度是不一样的,这就影射出空间的宽度可能会不一样。

好了,国际化的内容就不说了,请自行搜索。

复制代码
 1 /**
 2  *  根据SCNetworkReachabilityFlags这个网络标记来转换成我们在开发中经常使用的网络状态
 3      1.不能连接网络
 4      2.蜂窝连接
 5      3.WiFi连接
 6      4.未知连接
 7  */
 8 static AFNetworkReachabilityStatus AFNetworkReachabilityStatusForFlags(SCNetworkReachabilityFlags flags) {
 9     
10     // 是否能够到达
11     BOOL isReachable = ((flags & kSCNetworkReachabilityFlagsReachable) != 0);
12     
13     // 在联网之前需要建立连接
14     BOOL needsConnection = ((flags & kSCNetworkReachabilityFlagsConnectionRequired) != 0);
15     
16     // 是否可以自动连接
17     BOOL canConnectionAutomatically = (((flags & kSCNetworkReachabilityFlagsConnectionOnDemand ) != 0) || ((flags & kSCNetworkReachabilityFlagsConnectionOnTraffic) != 0));
18     
19     // 是否可以连接,在不需要用户手动设置的前提下
20     BOOL canConnectWithoutUserInteraction = (canConnectionAutomatically && (flags & kSCNetworkReachabilityFlagsInterventionRequired) == 0);
21     
22     // 是否可以联网的条件 1.能够到达 2.不需要建立连接或者不需要用户手动设置连接 就表示能够连接到网络
23     BOOL isNetworkReachable = (isReachable && (!needsConnection || canConnectWithoutUserInteraction));
24 
25     AFNetworkReachabilityStatus status = AFNetworkReachabilityStatusUnknown;
26     if (isNetworkReachable == NO) {
27         status = AFNetworkReachabilityStatusNotReachable;
28     }
29 #if    TARGET_OS_IPHONE
30     else if ((flags & kSCNetworkReachabilityFlagsIsWWAN) != 0) {
31         status = AFNetworkReachabilityStatusReachableViaWWAN;
32     }
33 #endif
34     else {
35         status = AFNetworkReachabilityStatusReachableViaWiFi;
36     }
37 
38     return status;
39 }
复制代码

这个方法根据SCNetworkReachabilityFlags这个标记转换成我们自定义的枚举类型。至于转换规则,上边的代码中注释部分写的很清楚。

*** 在这里不得不多说几句,很多框架中都会把一个类中的私有方法写成这样。为什么呢? 我们在开发中经常会写成- (void)funcName; 这样的私有方法。

我个人的意见是一个类中的私有方法写成static void funcName() 这样的c函数比较好。 

1. 在文件的最前方,比较容易查找

2. 可以适当的使用内联函数,提高效率。

 

根据一个标识 来处理Block和通知。保证两者同一状态。

包含了 类中需要处理的属性。

来看这个最基本的初始化方法,初始化了自身的属性。

CFRetain()后要记得CFRelease().

通过一个socket地址来初始化。 首先新建 SCNetworkReachabilityRef 对象,然后调用initWithReachability: 方法。记得手动管理内存。

 

这个方法基本同上。

综合上边两个方法,我们发现 SCNetworkReachabilityRef 有两个创建方法:

1. SCNetworkReachabilityCreateWithName 

2. SCNetworkReachabilityCreateWithAddress

 

由于IPv6 是ios9和os_x 10.11后边推出的,所有要进行版本判断。这礼拜呢设计到的socket的知识,请看 socket通信

通过这段代码我们能学到什么呢?

1,方法的创建也是有顺序的,可以使用函数访问函数的思想。

2. @if 这样的预编译指令能够替换掉代码中部分if else 。好处就是代码会不会被编译的区别。

单例的写法。

对需要释放时,做一些处理。

这个是.h文件暴露出来的3个BOOL 属性的getter方法,注意,由于我们在@property中定义了getter方法,所以getter方法就要写成我们定义的那种。

从这3个方法中也能看出,函数嵌套的思想还是很重要,要想做到这一点,只能是多想才行。

这个算是这个类的核心方法,设置监听网咯监听。

我们先来了解下基础知识。

SCNetworkReachabilityContext

点进去,会发现这是一个结构体,一般c语言的结构体是对要保存的数据的一种描述

 

1. 第一个参数接受一个signed long 的参数

2. 第二个参数接受一个void * 类型的值,相当于oc的id类型,void * 可以指向任何类型的参数

3. 第三个参数 是一个函数 目的是对info做retain操作,

4. 第四个参数是一个函数,目的是对info做release操作

5. 第五个参数是 一个函数,根据info获取Description字符串

在这里我们要携带的这个info就是下边的这个block

复制代码
 1 __weak __typeof(self)weakSelf = self;
 2     AFNetworkReachabilityStatusBlock callback = ^(AFNetworkReachabilityStatus status) {
 3         __strong __typeof(weakSelf)strongSelf = weakSelf;
 4 
 5         strongSelf.networkReachabilityStatus = status;
 6         if (strongSelf.networkReachabilityStatusBlock) {
 7             strongSelf.networkReachabilityStatusBlock(status);
 8         }
 9 
10     };
复制代码

retain和release 函数是下边的这两个函数

复制代码
1 static const void * AFNetworkReachabilityRetainCallback(const void *info) {
2     return Block_copy(info);
3 }
4 
5 static void AFNetworkReachabilityReleaseCallback(const void *info) {
6     if (info) {
7         Block_release(info);
8     }
9 }
复制代码

设置网络监控分为下边几个步骤:

1.我们先新建上下文

1 SCNetworkReachabilityContext context = {0, (__bridge void *)callback, AFNetworkReachabilityRetainCallback, AFNetworkReachabilityReleaseCallback, NULL};

2.设置回调

1 SCNetworkReachabilitySetCallback(self.networkReachability, AFNetworkReachabilityCallback, &context);

其中这个AFNetworkReachabilityCallback 是这样被定义的一个函数

typedef void (*SCNetworkReachabilityCallBack)    (
                        SCNetworkReachabilityRef            target,
                        SCNetworkReachabilityFlags            flags,
                        void                 *    __nullable    info
                        );

在本类中

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

3.加入RunLoop池

1 SCNetworkReachabilityScheduleWithRunLoop(self.networkReachability, CFRunLoopGetMain(), kCFRunLoopCommonModes);

其中CFRunLoopGetMain()代表主RunLoop

ok,差不多已经完成

 

在异步线程 发送一次当前的网络状态。

 

停止网络监控

这两个方法没什么好说的了,一个是getter 一个是setter 

注册键值依赖,这个可能大家平时用的比较少。可以了解一下

比如说一个类User中有两个属性

 

还有一个卡片的类card

 我们写一个info的setter 和 getter  方法,

复制代码
 1 @interface User :NSObject
 2 @property (nonatomic,copy)NSString *name;
 3 @property (nonatomic,assign)NSUInteger age;
 4 @end
 5 
 6 
 7 
 8 @interface card :NSObject
 9 @property (nonatomic,copy)NSString *info;
10 @property (nonatomic,strong)User *user;
11 @end
12 @implementation card
13 
14 - (NSString *)info {
15     return [NSString stringWithFormat:@"%@/%lu",_user.name,(unsigned long)_user.age];
16 }
17 - (void)setInfo:(NSString *)info {
18     
19     NSArray *array = [info componentsSeparatedByString:@"/"];
20     _user.name = array[0];
21     _user.age = [array[1] integerValue];
22     
23 }
24 
25 + (NSSet<NSString *> *)keyPathsForValuesAffectingValueForKey:(NSString *)key {
26     NSSet * keyPaths = [super keyPathsForValuesAffectingValueForKey:key];
27     NSArray * moreKeyPaths = nil;
28 
29     if ([key isEqualToString:@"info"])
30     {
31         moreKeyPaths = [NSArray arrayWithObjects:@"user.name", @"user.age", nil];
32     }
33 
34     if (moreKeyPaths)
35     {
36         keyPaths = [keyPaths setByAddingObjectsFromArray:moreKeyPaths];
37     }
38     
39     return keyPaths;
40 }
41 
42 @end
复制代码

代码差不多就是上边的。我们可以监听card的info属性,当user中的name或者age的值发生改变的时候,就会触发info的键值监听方法。这就是键值依赖的作用。

好了 本篇文章就到此为止了。下一篇会是AFSecurityPolicy

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值