欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  移动技术

IOS网络请求之AFNetWorking 3.x 使用详情

程序员文章站 2024-02-15 20:18:04
前言: 计划把公司的网络请求与业务解耦,所以想着学习一下网络请求,最近学习了nsurlsession,今天来学习一下基于nsurlsession封装的优秀开源框架afne...

前言:

计划把公司的网络请求与业务解耦,所以想着学习一下网络请求,最近学习了nsurlsession,今天来学习一下基于nsurlsession封装的优秀开源框架afnetworking 3.x,之前13年做ios开发时用的asihttprequest开源框架。

afnetworking

afnetworking一款轻量级网络请求开源框架,基于ios和mac os 网络进行扩展的高性能框架,大大降低了ios开发工程师处理网络请求的难度,让ios开发变成一件愉快的事情。

下载地址:afnetworking_jb51.rar

1.)afhttpsessionmanager请求管理者

-(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请求

-(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请求

-(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.)处理文件上传

-(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.)处理文件下载

-(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.)网络状态监听

- (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,如下

afhttpsessionmanager *manager = [afhttpsessionmanager manager];

其实我们点进去查看源码发现并不是单例,而是每次都实例化一个afhttpsessionmanager对象,源码如下

+ (instancetype)manager {
  return [[[self class] alloc] initwithbaseurl:nil];
}

所以我们在使用afnetworking的时候要对afhttpsessionmanager进行单例封装

+ (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文件中添加如下代码

<key>nsapptransportsecurity</key> 
  <dict> 
    <key>nsallowsarbitraryloads</key> 
    <true/> 
 </dict> 

第二种方式:

配置https ca证书,这里采用获取nsbundle中获取ca证书,afnetworking提供了配置afsecuritypolicy模块

+ (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的基本使用,方便以后查找。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。