前言:
计划把公司的网络请求与业务解耦,所以想着学习一下网络请求,最近学习了NSURLSession,今天来学习一下基于NSURLSession封装的优秀开源框架AFNetWorking 3.x,之前13年做iOS开发时用的ASIHttpRequest开源框架。
AFNetWorking
AFNetWorking一款轻量级网络请求开源框架,基于iOS和mac os 网络进行扩展的高性能框架,大大降低了iOS开发工程师处理网络请求的难度,让iOS开发变成一件愉快的事情。
下载地址:AFNetworking.rar
1.)AFHTTPSessionManager请求管理者
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
|
-(AFHTTPSessionManager *)sharedManager { AFHTTPSessionManager *manager = [AFHTTPSessionManager manager]; //最大请求并发任务数 manager.operationQueue.maxConcurrentOperationCount = 5; // 请求格式 // AFHTTPRequestSerializer 二进制格式 // AFJSONRequestSerializer JSON // AFPropertyListRequestSerializer PList(是一种特殊的XML,解析起来相对容易) manager.requestSerializer = [AFHTTPRequestSerializer serializer]; // 上传普通格式 // 超时时间 manager.requestSerializer.timeoutInterval = 30.0f; // 设置请求头 [manager.requestSerializer setValue:@ "gzip" forHTTPHeaderField:@ "Content-Encoding" ]; // 设置接收的Content-Type manager.responseSerializer.acceptableContentTypes = [[NSSet alloc] initWithObjects:@ "application/xml" , @ "text/xml" ,@ "text/html" , @ "application/json" ,@ "text/plain" ,nil]; // 返回格式 // AFHTTPResponseSerializer 二进制格式 // AFJSONResponseSerializer JSON // AFXMLParserResponseSerializer XML,只能返回XMLParser,还需要自己通过代理方法解析 // AFXMLDocumentResponseSerializer (Mac OS X) // AFPropertyListResponseSerializer PList // AFImageResponseSerializer Image // AFCompoundResponseSerializer 组合 manager.responseSerializer = [AFJSONResponseSerializer serializer]; //返回格式 JSON //设置返回C的ontent-type manager.responseSerializer.acceptableContentTypes=[[NSSet alloc] initWithObjects:@ "application/xml" , @ "text/xml" ,@ "text/html" , @ "application/json" ,@ "text/plain" ,nil]; return manager; } |
2.)处理get请求
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
-( void )doGetRequest { //创建请求地址 NSString *url=@ "http://api.nohttp.net/method" ; //构造参数 NSDictionary *parameters=@{@ "name" :@ "yanzhenjie" ,@ "pwd" :@ "123" }; //AFN管理者调用get请求方法 [[self shareAFNManager] GET:url parameters:parameters progress:^(NSProgress * _Nonnull downloadProgress) { //返回请求返回进度 NSLog(@ "downloadProgress-->%@" ,downloadProgress); } success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) { //请求成功返回数据 根据responseSerializer 返回不同的数据格式 NSLog(@ "responseObject-->%@" ,responseObject); } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) { //请求失败 NSLog(@ "error-->%@" ,error); }]; } |
3.)处理post请求
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
-( void )doPostRequestOfAFN { //创建请求地址 NSString *url=@ "http://api.nohttp.net/postBody" ; //构造参数 NSDictionary *parameters=@{@ "name" :@ "yanzhenjie" ,@ "pwd" :@ "123" }; //AFN管理者调用get请求方法 [[self shareAFNManager] POST:url parameters:parameters progress:^(NSProgress * _Nonnull uploadProgress) { //返回请求返回进度 NSLog(@ "downloadProgress-->%@" ,uploadProgress); } success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) { //请求成功返回数据 根据responseSerializer 返回不同的数据格式 NSLog(@ "responseObject-->%@" ,responseObject); } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) { //请求失败 NSLog(@ "error-->%@" ,error); }]; } |
4.)处理文件上传
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
|
-( void )doUploadRequest { // 创建URL资源地址 NSString *url = @ "http://api.nohttp.net/upload" ; // 参数 NSDictionary *parameters=@{@ "name" :@ "yanzhenjie" ,@ "pwd" :@ "123" }; [[self shareAFNManager] POST:url parameters:parameters constructingBodyWithBlock:^(id<AFMultipartFormData> _Nonnull formData) { NSDate* dat = [NSDate dateWithTimeIntervalSinceNow:0]; NSTimeInterval a=[dat timeIntervalSince1970]; NSString* fileName = [NSString stringWithFormat:@ "file_%0.f.txt" , a]; [FileUtils writeDataToFile:fileName data:[@ "upload_file_to_server" dataUsingEncoding:NSUTF8StringEncoding]]; // 获取数据转换成data NSString *filePath =[FileUtils getFilePath:fileName]; // 拼接数据到请求题中 [formData appendPartWithFileURL:[NSURL fileURLWithPath:filePath] name:@ "headUrl" fileName:fileName mimeType:@ "application/octet-stream" error:nil]; } progress:^(NSProgress * _Nonnull uploadProgress) { // 上传进度 NSLog(@ "%lf" ,1.0 *uploadProgress.completedUnitCount / uploadProgress.totalUnitCount); } success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) { //请求成功 NSLog(@ "请求成功:%@" ,responseObject); } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) { //请求失败 NSLog(@ "请求失败:%@" ,error); }]; } |
5.)处理文件下载
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
36
37
38
|
-( void )doDownLoadRequest { NSString *urlStr =@ "http://images2015.cnblogs.com/blog/950883/201701/950883-20170105104233581-62069155.png" ; // 设置请求的URL地址 NSURL *url = [NSURL URLWithString:urlStr]; // 创建请求对象 NSURLRequest *request = [NSURLRequest requestWithURL:url]; // 下载任务 NSURLSessionDownloadTask *task = [[self shareAFNManager] downloadTaskWithRequest:request progress:^(NSProgress * _Nonnull downloadProgress) { // 下载进度 NSLog(@ "当前下载进度为:%lf" , 1.0 * downloadProgress.completedUnitCount / downloadProgress.totalUnitCount); } destination:^NSURL * _Nonnull(NSURL * _Nonnull targetPath, NSURLResponse * _Nonnull response) { // 下载地址 NSLog(@ "默认下载地址%@" ,targetPath); //这里模拟一个路径 真实场景可以根据url计算出一个md5值 作为fileKey NSDate* dat = [NSDate dateWithTimeIntervalSinceNow:0]; NSTimeInterval a=[dat timeIntervalSince1970]; NSString* fileKey = [NSString stringWithFormat:@ "/file_%0.f.txt" , a]; // 设置下载路径,通过沙盒获取缓存地址,最后返回NSURL对象 NSString *filePath = [FileUtils getFilePath:fileKey]; return [NSURL fileURLWithPath:filePath]; // 返回的是文件存放在本地沙盒的地址 } completionHandler:^(NSURLResponse * _Nonnull response, NSURL * _Nullable filePath, NSError * _Nullable error) { // 下载完成调用的方法 NSLog(@ "filePath---%@" , filePath); NSData *data=[NSData dataWithContentsOfURL:filePath]; UIImage *image=[UIImage imageWithData:data]; // 刷新界面... UIImageView *imageView =[[UIImageView alloc]init]; imageView.image=image; [self.view addSubview:imageView]; [imageView mas_makeConstraints:^(MASConstraintMaker *make) { make.center.equalTo(self.view); make.size.mas_equalTo(CGSizeMake(300, 300)); }]; }]; //启动下载任务 [task resume]; } |
6.)网络状态监听
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
36
37
38
39
40
41
|
- ( void )aFNetworkStatus{ //创建网络监测者 AFNetworkReachabilityManager *manager = [AFNetworkReachabilityManager sharedManager]; /*枚举里面四个状态 分别对应 未知 无网络 数据 WiFi typedef NS_ENUM(NSInteger, AFNetworkReachabilityStatus) { AFNetworkReachabilityStatusUnknown = -1, 未知 AFNetworkReachabilityStatusNotReachable = 0, 无网络 AFNetworkReachabilityStatusReachableViaWWAN = 1, 蜂窝数据网络 AFNetworkReachabilityStatusReachableViaWiFi = 2, WiFi }; */ [manager setReachabilityStatusChangeBlock:^(AFNetworkReachabilityStatus status) { //这里是监测到网络改变的block 可以写成switch方便 //在里面可以随便写事件 switch (status) { case AFNetworkReachabilityStatusUnknown: NSLog(@ "未知网络状态" ); break ; case AFNetworkReachabilityStatusNotReachable: NSLog(@ "无网络" ); break ; case AFNetworkReachabilityStatusReachableViaWWAN: NSLog(@ "蜂窝数据网" ); break ; case AFNetworkReachabilityStatusReachableViaWiFi: NSLog(@ "WiFi网络" ); break ; default : break ; } }] ; [manager startMonitoring]; } |
AFNetWorking内存泄露
通常情况我们一般会认为以manager结尾的都是单例模式,所以我们一般都是这样使用AFNetWorking,如下
1
|
AFHTTPSessionManager *manager = [AFHTTPSessionManager manager]; |
其实我们点进去查看源码发现并不是单例,而是每次都实例化一个AFHTTPSessionManager对象,源码如下
1
2
3
|
+ (instancetype)manager { return [[[self class ] alloc] initWithBaseURL:nil]; } |
所以我们在使用AFNetWorking的时候要对AFHTTPSessionManager进行单例封装
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
+ (AFHTTPSessionManager *)sharedManager { static AFHTTPSessionManager *manager = nil; static dispatch_once_t predicate; dispatch_once(&predicate, ^{ manager = [AFHTTPSessionManager manager]; manager.operationQueue.maxConcurrentOperationCount = 5; manager.requestSerializer.timeoutInterval=30.f; manager.responseSerializer.acceptableContentTypes = [[NSSet alloc] initWithObjects:@ "application/xml" , @ "text/xml" ,@ "text/html" , @ "application/json" ,@ "text/plain" ,nil]; [manager.requestSerializer setValue:@ "gzip" forHTTPHeaderField:@ "Content-Encoding" ]; }); return manager; } |
AFNetWorking关于HTTPS
在2017年1月1日起Apple 要求开发者于年底之前为提交至 App Store 中的应用启用 HTTPS ,以支持 iOS 9 引入的 ATS(App Transport Security)技术。但后来,apple 发布声明宣布延长这个时限,提供给开发者更多的时间进行相关准备。目前 Apple 尚未公布新的截止日期。所以目前应对https的方案有两种。
第一种方式:
屏蔽调iOS ATS(App Transport Security),在pList.info文件中添加如下代码
1
2
3
4
5
|
< key >NSAppTransportSecurity</ key > < dict > < key >NSAllowsArbitraryLoads</ key > < true /> </ dict > |
第二种方式:
配置https CA证书,这里采用获取NSBundle中获取CA证书,AFNetWorking提供了配置AFSecurityPolicy模块
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
+ (AFSecurityPolicy *)customSecurityPolicy{ //Https CA证书地址 NSString *cerPath = [[NSBundle mainBundle] pathForResource:@ "XueLeTSHTTPS" ofType:@ "cer" ]; //获取CA证书数据 NSData *cerData = [NSData dataWithContentsOfFile:cerPath]; //创建AFSecurityPolicy对象 AFSecurityPolicy *security = [AFSecurityPolicy policyWithPinningMode:AFSSLPinningModeNone]; //设置是否允许不信任的证书(证书无效、证书时间过期)通过验证 ,默认为NO. security.allowInvalidCertificates = YES; //是否验证域名证书的CN(common name)字段。默认值为YES。 security.validatesDomainName = NO; //根据验证模式来返回用于验证服务器的证书 security.pinnedCertificates = [NSSet setWithObject:cerData]; return security; } |
然后通过设置AFHTTPSessionManager的securityPolicy属性等于自定义的AFSecurityPolicy。
总结:
简单记录一下AFNetWorking的基本使用,方便以后查找。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持服务器之家。
原文链接:http://www.cnblogs.com/whoislcj/p/6377158.html