结构

AFURLResponseSerialization负责解析网络返回数据,检查数据是否合法,把NSData数据转成相应的对象,内置的转换器有json,xml,plist,image,用户可以很方便地继承基类AFHTTPResponseSerializer去解析更多的数据格式,AFNetworking这一套响应解析机制结构很简单,主要就是两个方法:

1.-validateResponse:data:error:

基类AFHTTPResponseSerializer的这个方法检测返回的HTTP状态码和数据类型是否合法,属性acceptableStatusCodes和acceptableContentTypes规定了合法的状态码和数据类型,例如JSONSerialization就把acceptableContentTypes设为@”application/json”, @”text/json”, @”text/javascript”,若不是这三者之一,就验证失败,返回相应的NSError对象。一般子类不需要重写这个方法,只需要设置好acceptableStatusCodes和acceptableContentTypes就行了。

2.-responseObjectForResponse:data:error:

这个方法解析数据,把NSData转成相应的对象,上层AFURLConnectionOperation会调用这个方法获取转换后的对象。

在解析数据之前会先调上述的validateResponse方法检测HTTP响应是否合法,要注意的是即使这里检测返回不合法,也会继续解析数据生成对象,因为有可能错误信息就在返回的数据里。

如果validateResponse返回error,这里的解析数据又出错,这时有两个error对象,怎样返回给上层?这里的处理是把解析数据的NSError对象保存到validateResponse NSError的userInfo里,作为UnderlyingError,NSError专门给了个NSUnderlyingErrorKey作为这种错误包含错误的键值。

剩下的就是NSecureCoding相关方法了,如果子类增加了property,需要加上相应的NSecureCoding方法。

JSON解析

AFJSONResponseSerializer使用系统内置的NSJSONSerialization解析json,NSJSON只支持解析UTF8编码的数据(还有UTF-16LE之类的,都不常用),所以要先把返回的数据转成UTF8格式。这里会尝试用HTTP返回的编码类型和自己设置的stringEncoding去把数据解码转成字符串NSString,再把NSString用UTF8编码转成NSData,再用NSJSONSerialization解析成对象返回。

上述过程是NSData->NSString->NSData->NSObject,这里有个问题,如果你能确定服务端返回的是UTF8编码的json数据,那NSData->NSString->NSData这两步就是无意义的,而且这两步进行了两次编解码,很浪费性能,所以如果确定服务端返回utf8编码数据,就建议自己再写个JSONResponseSerializer,跳过这两个步骤。

此外AFJSONResponseSerializer专门写了个方法去除NSNull,直接把对象里值是NSNull的键去掉,还蛮贴心,若不去掉,上层很容易忽略了这个数据类型,判断了数据是否nil没判断是否NSNull,进行了错误的调用导致core。

图片解压

当我们调用UIImage的方法imageWithData:方法把数据转成UIImage对象后,其实这时UIImage对象还没准备好需要渲染到屏幕的数据,现在的网络图像PNG和JPG都是压缩格式,需要把它们解压转成bitmap后才能渲染到屏幕上,如果不做任何处理,当你把UIImage赋给UIImageView,在渲染之前底层会判断到UIImage对象未解压,没有bitmap数据,这时会在主线程对图片进行解压操作,再渲染到屏幕上。这个解压操作是比较耗时的,如果任由它在主线程做,可能会导致速度慢UI卡顿的问题。

AFImageResponseSerializer除了把返回数据解析成UIImage外,还会把图像数据解压,这个处理是在子线程(AFNetworking专用的一条线程,详见AFURLConnectionOperation),处理后上层使用返回的UIImage在主线程渲染时就不需要做解压这步操作,主线程减轻了负担,减少了UI卡顿问题。

具体实现上在AFInflatedImageFromResponseWithDataAtScale里,创建一个画布,把UIImage画在画布上,再把这个画布保存成UIImage返回给上层。只有JPG和PNG才会尝试去做解压操作,期间如果解压失败,或者遇到CMKY颜色格式的jpg,或者图像太大(解压后的bitmap太占内存,一个像素3-4字节,搞不好内存就爆掉了),就直接返回未解压的图像。

另外在代码里看到iOS才需要这样手动解压,MacOS上已经有封装好的对象NSBitmapImageRep可以做这个事。

关于图片解压,还有几个问题不清楚:

1.本来以为调用imageWithData方法只是持有了数据,没有做解压相关的事,后来看到调用堆栈发现已经做了一些解压操作,从调用名字看进行了huffman解码,不知还会继续做到解码jpg的哪一步。

2.以上图片手动解压方式都是在CPU进行的,如果不进行手动解压,把图片放进layer里,让底层自动做这个事,是会用GPU进行的解压的。不知用GPU解压与用CPU解压速度会差多少,如果GPU速度很快,就算是在主线程做解压,也变得可以接受了,就不需要手动解压这样的优化了,不过目前没找到方法检测GPU解压的速度。

P.S. 关于图片解压,有篇挺挺不错的文章:Avoiding Image Decompression Sickness

源码注释

1
AFURLResponseSerialization.m

AFNetworking2.0源码解析<四>的更多相关文章

  1. AFNetworking2.0源码解析<一>

    本篇先看看AFURLConnectionOperation,AFURLConnectionOperation继承自NSOperation,是一个封装好的任务单元,在这里构建了NSURLConnecti ...

  2. AFNetworking2.0源码解析<三>

    本篇说说安全相关的AFSecurityPolicy模块,AFSecurityPolicy用于验证HTTPS请求的证书,先来看看HTTPS的原理和证书相关的几个问题. HTTPS HTTPS连接建立过程 ...

  3. AFNetworking2.0源码解析<二>

    本篇我们继续来看看AFNetworking的下一个模块 — AFURLRequestSerialization.   AFURLRequestSerialization用于帮助构建NSURLReque ...

  4. AFNetworking (3.1.0) 源码解析 <四>

    这次主要看一下文件夹Security中的类AFSecurityPolicy----安全策略类. AFSecurityPolicy主要的作用是验证HTTPS请求证书的有效性,在iOS9之后,默认不能发送 ...

  5. solr&lucene3.6.0源码解析(四)

    本文要描述的是solr的查询插件,该查询插件目的用于生成Lucene的查询Query,类似于查询条件表达式,与solr查询插件相关UML类图如下: 如果我们强行将上面的类图纳入某种设计模式语言的话,本 ...

  6. Sentinel源码解析四(流控策略和流控效果)

    引言 在分析Sentinel的上一篇文章中,我们知道了它是基于滑动窗口做的流量统计,那么在当我们能够根据流量统计算法拿到流量的实时数据后,下一步要做的事情自然就是基于这些数据做流控.在介绍Sentin ...

  7. solr&lucene3.6.0源码解析(三)

    solr索引操作(包括新增 更新 删除 提交 合并等)相关UML图如下 从上面的类图我们可以发现,其中体现了工厂方法模式及责任链模式的运用 UpdateRequestProcessor相当于责任链模式 ...

  8. Heritrix 3.1.0 源码解析(三十七)

    今天有兴趣重新看了一下heritrix3.1.0系统里面的线程池源码,heritrix系统没有采用java的cocurrency包里面的并发框架,而是采用了线程组ThreadGroup类来实现线程池的 ...

  9. Android事件总线(二)EventBus3.0源码解析

    1.构造函数 当我们要调用EventBus的功能时,比如注册或者发送事件,总会调用EventBus.getDefault()来获取EventBus实例: public static EventBus ...

随机推荐

  1. SWPU2019 伟大的侦探

    01editor 选择 EBCDIC编码得到压缩包的密码 这里用到的是福尔摩斯里面的跳舞的小人加密 结果是:iloveholmesandwllm

  2. 管理es索引-使用 Xput创建索引

    curl是利用URL语法在命令行方式下工作的开源文件传输工具,使用curl可以简单实现常见的get/post请求.简单的认为是可以在命令行下面访问url的一个工具.在centos的默认库里面是有cur ...

  3. springMVC的常用注解有哪些?

    1.@Controller @Controller 用于标记在一个类上,使用它标记的类就是一个SpringMVC Controller 对象.分发处理器将会扫描使用了该注解的类的方法,并检测该方法是否 ...

  4. python面向对象之封装,多态与继承

    一.继承,包括单继承和多继承 对于面向对象的继承来说,其实就是将多个类共有的方法提取到父类中,子类 仅需继承父类而不必一一实现每个方法. 实例: #coding=utf-8 class Person( ...

  5. 使用Map接收返回数据库的数据

    查询返回值是map类型的一条数据 1 首先在接口中写方法 public interface EmployeeMapper { //返回一条记录的map:key就是列名,值就是对应的值 public M ...

  6. ssm项目dao层方法异常:org.apache.ibatis.binding.BindingException: Invalid bound statement

    在IntelliJ IDEA中用ssm框架搭建了一个demo项目,在执行到dao层方法时抛出这个异常: org.apache.ibatis.binding.BindingException: Inva ...

  7. 【MM系列】SAP MRKO如何操作

    公众号:SAP Technical 本文作者:matinal 原文出处:http://www.cnblogs.com/SAPmatinal/ 原文链接:[MM系列]SAP MRKO如何操作   前言部 ...

  8. C#新特性span 和 Tuple

    span 可用于高性能字符串分割等 https://www.cnblogs.com/lonelyxmas/p/10171869.html https://www.codemag.com/article ...

  9. codeforces 1186C Vus the Cossack and Strings

    题目链接:https://codeforc.es/contest/1186/problem/C 题目大意:xxxxx(自认为讲不清.for instance) 例如:a="01100010& ...

  10. zip函数用于对列表对应元素打包成元组

    zip() 函数用于将可迭代的对象作为参数,将对象中对应的元素打包成一个个元组,然后返回由这些元组组成的列表. 如果各个迭代器的元素个数不一致,则返回列表长度与最短的对象相同,利用 * 号操作符,可以 ...