今天在项目中发现一个ASIHttpRequest的Bug。这个Bug可能会导致你Http请求延时至少在timeout设置时间结束之后。更可怕的是,为了找出这个Bug,可能会浪费程序员更多的时间。为这个问题我至少奋斗了3个小时!
项目中一直在使用ASIHttpRequest,但有一次,我发现Http请求居然总是不成功。我下意识地以为这是网络延时问题,我延长了ASIHttpRequest的超时时间至30秒,于是程序不再报错。
但今天在使用同样的代码时,我发现以前我全错了。这不是超时的问题,而是ASIHttpRequest的一个Bug。
因为这次我请求本机的Tomcat服务器的某个jsp页面,页面代码写得很简单,在firefox中请求该页面的响应时间不会超过0.01秒,但在iPhone模拟器中居然用了20多秒才返回。这不可能是网络延时的原因。
当我把超时时间改小至10秒,等待超时时间结束,ASIHttpRequest甚至直接报kCFStreamEventErrorOccurred错误。
我打开ASIHttpRequest的所有调试日志,发现ASIHttpRequest似乎在尝试用上次的连接去连接服务器:
Request #3 will use connection #2
Request attempted to use connection #2, but it has been closed -will retry with a new connection
Request #3 will use connection #3
它好像连接时重用上次的http连接。这样肯定是不行的,因为上次的http请求结束后服务器已经把连接关闭了。
于是我们只能把ASIHttpRequest的连接重用关闭:
[requestsetShouldAttemptPersistentConnection:NO];
这样,前面的问题就不会出现了。其实,在不同的http会话中,不应该开启PersistentConnection。
2,iOS使用ASIHTTPRequest请求过来的数据中包含utf-8字符
在使用ASIHTTPRequest请求一个接口时,使用NLog打印出返回的Json串,发现有很少一部分汉字显示的是其UTF-8编码的样式,比如“考试”会显示为“考350257225”,其中350257225是“试”utf-8编码,而且没有规律,有些时候这个字显示不正常,有时候显示正常,Android上没有这种问题,在服务端打印出生成的JSON也是正常的,所以接口返回值应该没有问题。服务器返回头的编码是UTF-8,ASIHTTPRequest也设置了UTF8编码:
request.defaultResponseEncoding = NSUTF8StringEncoding;
现在怀疑是 ASIHTTPRequest的问题,不知大家谁遇见过这样的问题,谢谢!
3,谨慎使用ASIHttpRequest的block特性
使用ASIHttpRequest的setCompletionBlock、setFailedBlock时碰到一些诡异的内存泄漏和莫名其妙的行为(如:无法release对象)。经过与同事的讨论、查找资料终于得以解决,全是对block理解不透彻惹的祸。
1. 声明ASIHttpRequest时一定要使用__block关键字
__block关键字告诉block不要retain request,这对于防止循环retain非常重要!!因为request总是会retain block.
2. 谨慎处理block与对象的关系
当setCompletionBlock/setFailedBlock内部使用对象的instance var时,self会被retain(If you access an instance variable by reference, self is retained;)。所以在request结束前向对象发送release消息不会导致对象的释放(dealloc),亦即:该对象依然可进行所有操作,这将导致诸多你意想不到的结果。
3. 解决第2点的问题
3.1 仔细拿捏block与对象的关系 + 按值的方式访问instance var(If you access an instance variable by value, the variable is retained.)
3.2 不使用block,而使用ASIHttpRequestDelegate
4. 参考资料
4.1 http://allseeing-i.com/ASIHTTPRequest/How-to-use - Using blocks
4.2 Blocks Programming Topics - Object and Block Variables