一個簡單的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的理解更透徹。