ASIHTTPRequest的startAsynchronous调用EXC_BAD_ACCESS

jopen 11年前

一个简单的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