AfNetworking 3.0源码解读

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

接下来咱们就很是详细的来读一读这个框架的代码,咱们的目标就是理解了它的思想以后,可以明白咱们的请求是如何实现的,咱们的代码哪里还须要进行改进,若是可以更进一步,咱们可以总结出一套适合大部分应用的网络架构思想。ios

可以让一些人从中受益。编程

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

咱们在看一个框架的时候呢,能够这样先看,先看每一个文件的头文件,也就是.h文件api

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

比较独立的类有:架构

1.AFURLResponseSerialization.happ

2.AFNetworkReachabilityManager.h框架

3.AFURLRequestSerialization.hdom

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:(const void *)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的键值监听方法。这就是键值依赖的做用。

相关文章
相关标签/搜索