来说说AFNetworking

大概分析一下AFNetworking功能和使用流程。

使用苹果的 NSURLSession 发起一个 POST 请求大概是以下这种方式:

// 创建NSMutableURLRequest对象,给它设置请求头、请求体
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:[NSURL URLWithString:URL]];
[request setHTTPMethod:@"POST"];
[request setHTTPBody:[@"pram1=p1" dataUsingEncoding:NSUTF8StringEncoding]];

// 创建NSURLSession对象,设置配置信息、代理和代理回调线程
NSURLSessionConfiguration *config = [NSURLSessionConfiguration defaultSessionConfiguration];
NSURLSession *session = [NSURLSession sessionWithConfiguration:config delegate:nil delegateQueue:[NSOperationQueue mainQueue]];

// 使用session去生成一个网络请求任务NSURLSessionDataTask,通过 block 或代理回调结果
NSURLSessionDataTask *task = [session dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
    NSDictionary *dict = [NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:nil];
    NSLog(@"%@",dict);
}];
// 开启这个请求任务
[task resume];

AFNetworking 的请求方式:

AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
[manager POST:URL parameters:@{@"pram1":@"p1"} progress:nil success:^(NSURLSessionDataTask * _Nonnull task, id  _Nullable responseObject) {
    NSLog(@"%@",responseObject);
} failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
    NSLog(@"%@",error);
}];

创建好AFHTTPSessionManager对象之后,直接进行 GET/POST 等请求。请求和响应都被封装了起来。

AFNetworking的封装
请求封装:

AFURLRequestSerialization负责封装请求参数,通过传入的URL、请求类型、请求参数创建一个request ,并设置请求头,一般设置以下几个请求头参数:

  • Accept-Language:
    告诉服务器本地支持的语言类型。一般格式如下:
    Accept-Language zh-cn,zh;q=0.8,en-us;q=0.5,en;q=0.3,zh-cn是简体中文,zh是中文,q的值越大表示越倾向该语言。
  • User-Agent:
    用户代理,简称UA。它里边包含了一些本地信息,如浏览器类型、操作系统及版本、CPU 类型等,可自行添加其它需要的信息,如网络环境。
  • Authorization :
    这个是可选项,如果访问的服务器是通过Basic Auth进行身份验证的,可以把账号密码通过它添加进请求头中。一般都使用https,因为http直接暴露了账号密码。
  • Content-Type:
    常用的 Content-Type 有 以下几种:
    text/plain; charset=utf-8,表示请求体是纯文本格式,utf-8编码;
    application/json; charset=utf-8,表示请求体是JSON字符串,如{"key":"value"};
    application/x-www-form-urlencoded; charset=utf-8,表示请求体是键值对格式,如"key"="value",这是AFNetworking默认的请求体格式;
    multipart/form-data; charset=utf-8; boundary=Boundary_XX,表示上传文件,一般上传的文件都比较大,需要分割,boundary表示分割符。

AF中共有三个类负责创建 request :
AFHTTPRequestSerializer
AFJSONRequestSerializer
AFPropertyListRequestSerializer

1、默认是通过 AFHTTPRequestSerializer 创建,它会把通过字典传入的参数转换为键值对的形式 key1=value1&key2=value2,key 和 value 都是经过编码的。

如果是 GET 请求,参数会通过 ?直接拼接在 URL 后面,如:http://localhost?key1=value1&key2=value2

如果 POST 请求,AF 会把 Content-Type 设置为 application/x-www-form-urlencoded; charset=utf-8,然后把参数通过 UTF-8 编码后放入请求体中。

2、若通过 AFJSONRequestSerializer 创建,GET 请求与上面相同,POST 请求时,AF 会把 Content-Type 设置为 application/json; charset=utf-8,参数直接使用NSJSONSerialization进行序列化,然后放入请求体中。

3、若通过 AFPropertyListRequestSerializer 创建,Content-Type 设置为 application/x-plist,参数通过NSPropertyListSerialization序列化后放入请求体。

开发中用的最多的是前两种序列化方式,第三种我还从来没用过。

上传文件:

上传文件有严格的格式要求:

(开头分割线 "--" 加 "boundary" ,此处的boundary是Boundaryxxx)
--Boundaryxxx 
(文件参数)
Content-Disposition: form-data; name="file"; filename="picture.png" 
(文件类型)
Content-Type: image/png
(文件二进制数据)
...png datas...
(结束分割线 "--" 加 "boundary" 加 "--")
--Boundaryxxx--

其中第二步的 form-data 就像 application/json 一样表示上传格式,name 是接口参数,比如服务器要求上传文件的参数名为 "abc",就要设置 name="abc",filename 是文件的名字。

若有多个文件,重复前四步即可。如前面所说,上传文件要给request设置请求头 Content-Type 为 multipart/form-data; boundary=Boundaryxxx。boundary 就是分割数据时设置的标示。

AF 提供了几个不同的上传方法。
(具体的文件读取有些麻烦,以后再写)

未完待续。。。

响应封装:

AFURLResponseSerialization类负责响应数据处理。在 AFURLSessionManager 中创建好 dataTask 后会为这个 dataTask 设置代理AFURLSessionManagerTaskDelegate,每个 AFURLSessionManager 都有一个代理。

NSURLSession 可以添加大量的 NSURLSessionTask 在后台下载,想要检查某个任务进度,只需 double taskProgress = (double)task.countOfBytesReceived / (double)task.countOfBytesExpectedToReceive;就能计算出来。

dataTask 的数据接收和接收完毕代理方法都会被转接到代理类中,代理对象会创建观察者监听 dataTask (NSURLSessionTask对象) 的几个属性,当发送/接收到数据时、任务状态改变时,这些数据会发生变化,这样就能监听到 上传/下载进度 和 取消/开启任务。

请求完毕后使用responseSerializer开始对结果进行解析,AF 默认使用 AFJSONResponseSerializer 类解析,它的默认可解析类型有:@"application/json", @"text/json", @"text/javascript"。

一般情况下我们的服务器返回的都是 JSON 数据,不过有时候也会返回一些其他类型的数据,导致 AF 不能被解析而返回错误,这就有了网上的一些解决办法:

AFHTTPSessionManager *manager =[AFHTTPSessionManager manager];
manager.responseSerializer.acceptableContentTypes = [NSSet setWithObjects:@"application/json", @"text/json", @"text/javascript", @"text/html",@"text/plain", nil];

十分地简单粗暴😆。

除了 AFJSONResponseSerializer,还有一些其它解析类:
AFHTTPResponseSerializer,直接返回 NSData 数据;
AFXMLParserResponseSerializer,使用 NSXMLParser 进行解析
AFPropertyListResponseSerializer,使用 NSPropertyListSerialization 进行解析;
AFImageResponseSerializer,解析成 Image。

解析完成后,通过completionHandler回调结果。

平时最常用的 GET/POST 请求流程说完了,说下其它功能吧。

网络监听:

AFNetworkReachabilityManager 类用来监听网络变化,可以单独拉出来使用。它的原理跟苹果官方的 Reachability 相同,都是用 SCNetworkReachability 来进行监听的。

不过 Reachability 并没有那么强大,苹果解释说它并不能真正检测到程序是否可以连接到服务器,只能检测到接口是否可连接,以及接口的网络类型。

也就是说,程序只连接到路由器,而路由器没有连接到外网,是检测不出来的;网络信号差,无法真正和服务器连接通信,也检测不出来。网上有人给出一种解决方案:发送一个真实的网络请求,或者使用socket编程,建立一个tcp链接来检测(三次握手成功),只要链接成功则服务器可达。这样只会发送tcpip的报头,数据量最小。如果网络环境差,connect函数会阻塞,所以最好不要在主线程下,调用示例代码:

#import <arpa/inet.h>
/// 服务器可达返回true
- (BOOL)socketReachabilityTest {
    // 客户端 AF_INET:ipv4  SOCK_STREAM:TCP链接
    int socketNumber = socket(AF_INET, SOCK_STREAM, 0);
    // 配置服务器端套接字
    struct sockaddr_in serverAddress;
    // 设置服务器ipv4
    serverAddress.sin_family = AF_INET;
    // 百度的ip
    serverAddress.sin_addr.s_addr = inet_addr("202.108.22.5");
    // 设置端口号,HTTP默认80端口
    serverAddress.sin_port = htons(80);
    if (connect(socketNumber, (const struct sockaddr *)&serverAddress, sizeof(serverAddress)) == 0) {
        close(socketNumber);
        return true;
    }
    close(socketNumber);;
    return false;
}

GitHub上也有检测网络真实环境的轮子如RealReachability

未完待续。。。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,524评论 5 460
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,869评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,813评论 0 320
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,210评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,085评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,117评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,533评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,219评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,487评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,582评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,362评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,218评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,589评论 3 299
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,899评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,176评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,503评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,707评论 2 335

推荐阅读更多精彩内容

  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,497评论 18 139
  • AFNetworking2.0源码解析<一> AFNetworking2.0源码解析<二>-- 构建普通请求: 格...
    钱嘘嘘阅读 1,754评论 0 4
  • 218.241.181.202 wxhl60 123456 192.168.10.253 wxhl66 wxhl6...
    CYC666阅读 1,343评论 0 6
  • iOS开发系列--网络开发 概览 大部分应用程序都或多或少会牵扯到网络开发,例如说新浪微博、微信等,这些应用本身可...
    lichengjin阅读 3,619评论 2 7
  • 今天想要讲的东西比较杂乱,自己理了好久的思路感觉一直找不到一条线串联起这些碎片化的知识。然后就想着那就先写写看吧,...
    Programmer客栈阅读 2,058评论 0 9