ASIHTTPRequest的startAsynchronous调用EXC_BAD_ACCESS
一个简单的iOS应用,在主视图控制器类中,实现了ASIHTTPRequest的委托方法- (void)requestFinished:(ASIHTTPRequest )request和- (void)requestFailed:(ASIHTTPRequest )request。
在这个类的viewDidLoad方法中,调用ASIHTTPRequest的异步请求。
请求对象的定义为retain,如@property(retain)ASIHTTPRequest theRequest;
在- (void)viewDidLoad方法中,调用的类中的方法是这个。
- (IBAction)showCheckcode:(id)sender {
NSString imgURL=@"https://dynamic.12306.cn/otsweb/passCodeAction.do?rand=sjrand";
self.theRequest=[ASIHTTPRequest requestWithURL:[NSURL URLWithString:imgURL]] ;
self.theRequest.accessibilityLabel=@"imgCode";
[self.theRequest setTag:100];
[self.theRequest setValidatesSecureCertificate:NO];
//[theRequest applyCookieHeader];
self.theRequest.delegate=(id<ASIHTTPRequestDelegate>)self;
[self.theRequest startAsynchronous];
}
并且,在- (void)viewDidLoad方法中,又调用另外一个自定义类的方法,请求一个异步操作。而且这个类也实现了ASIHTTPRequest的委托方法。
应用一运行,就出现错误,在控制台中看到错误信息是EXC_BAD_ACCESS。
遇到问题第一个反应应该是,冷静。
虽然这个问题很麻烦,明显是和ASIHTTPRequest相关,而它是一个开源的框架,不是我所熟悉的。
ASIHTTPRequest的开发说不再维护它了,但ASIHTTPRequest也不失为一个非常好的网络访问框架,而且对于一般应用是足够的。 ASIHTTPRequest是基于MRC下,我的应用是ARC下,所以在将ASIHTTPRequest拖进应用包时,需要做一个简单的调整。
1.选择target,然后选择Build Phases标签,展开Compile Sources;
2.在所有和ASI..相关的文件后面的(Compile Flags)加入编译选项”-fno-objc-arc”
对了,我的开发工具是XCODE4.5。这里补充一下。
分析一下ASIHTTPRequest的框架,将ASIHTTPRequestConfig.h文件中配置调试项打开,再运行下。
虽然应用还是crash,但这回错误停在ASIHTTPRequest.m中一个方法上。
(void)requestReceivedResponseHeaders:(NSMutableDictionary )newResponseHeaders {
if ([self error] || [self mainRequest]) { return; }
--> if (delegate && [delegate respondsToSelector:didReceiveResponseHeadersSelector]) {
问题出在delegate的设置上。
在stackoverflow.com上,有网友提出了两个解决方法。
第一个,使用同一的delegate,并且应用一运行就初始化它。如果其他类要调用异步请求,将delegate发过去。
调用方法如下:
- (void)sendUrl: (NSString ) restUrl withCallBack:(NSObject) delegate;
然后,在delegate的实现方法中利用request的tag来标识来自哪一个类的异步调用。
第二个,使用block直接实现异步请求,抛弃delegate。
使用"setCompletionBlock:^"代替delegate的"setDidFinishSelector"。
request做一个设置就可以,比原来通过设置request.delegate再实现delegate的方法简单多了。
[request setCompletionBlock :^{
// 请求响应结束,返回 responseString
NSString responseString = [request responseString ]; // 对于 2 进制数据,使用 NSData 返回 NSData responseData = [request responseData];
NSLog ( @"%@" ,responseString);
}];
[request setFailedBlock :^{
// 请求响应失败,返回错误信息
NSError error = [request error ];
NSLog ( @"error:%@" ,[error userInfo ]);
}];
可见第二个方法比第一个方便多了。在iOS5.0版本中,就可以使用block了。没想到block这么管用。
问题解决,按照个人惯例,需要简单总结一下。
1、在ARC怎么调用以前MRC开发的开源框架;
2、framework怎么编译,真机和模拟机的差别,release版和debug版的差别。这点在文档中没有提及,但也做了尝试,虽然结果很失败。
3、多线程,ASIHTTPRequest的异步请求就是多线程调用。4、NSOperationQueue的一些基础,但现在还不明白。
5、delegate的理解更透彻。
参考资料:http://stackoverflow.com/questions/5701132/asihttprequest-crashes-my-app