就個人經驗而言,在iphone線程中使用異步NSURLConnection的經驗可以說是一個完全和愉悅搭不上邊的事情。他給我帶來的麻煩可真不少。例如,前幾天,幫客戶定位一個問題的時候發生的事情。
事情經過是這樣的:客戶反饋,無法正常使用我們提供的某個和網絡相關的功能,網絡回調沒有收到。但是其他回調可以正常工作,并且所有回調都是以同樣的邏輯放在某個地方的。
我先確認了他的使用方式是否正確,并確認了輸入參數的正確性,并且驗證了回調的正確設置以及回調函數的使用無誤。一切都沒有問題,但是就是無法收到回調。
一切都那么的神奇,功能的調用并沒有什么特殊的,但是就單單這個功能工作不正常。中間又經過一些確認,發現數據并沒有到達服務端。但是同樣的使用方式在我們自己的環境下工作又是正常的。
花了將近5個小時,還是沒有發現原因,就在我準備放棄的時候,突然想到,莫非他的調用位置是線程中調用?經過確認,發現果然是在線程中調用的!讓他們使用performOnMainThread的方式調用,終于解決了問題。
以前遇到過一次在線程中調用異步網絡的情況,請教同事,同事告知,異步網絡需要自己的RunLoop,所以要在線程中使用異步網絡必須有自己的RunLoop,可以將他放到主線程的RunLoop。
但是,這樣子,多線程的性能必然被降低,因為這樣網絡的工作就都是在主線程中完成的。同時懷疑,這樣子的網絡性能設計不是很低下!
剛好有點時間,就仔細的翻了一下蘋果的開發文檔,發現其中指出,所有的NSThread都有一個屬于自己的NSRunLoop,而NSURLConnection的回調都會回調到當前線程中!
這個和我目前遇到的完全不一樣!我無法在當前線程中收到回調!
又仔細在網絡上搜索之后發現,原來,是因為網絡回調的時候線程的NSRunLoop已經被無效的原因。
我們常見的線程中網絡調用是這樣的:
- (void)threadFunc
{
NSAutoReleasePool *pool = [NSAutoReleasePool new];
//do something
……
//send your request
[NSURLConnection connectionWithRequest:xxx];
//do some other thing
……
[pool release];
}
一般來說,這個函數會很快走完,線程就結束了。所以,當網絡響應回來的時候,你的線程已經結束了。你的網絡回調依賴于該線程的NSRunLoop,但是線程已經結束了,所以你的網絡回調就無法收到!!同理,所有的performAfterDelay之類的api以及NSTimer的在線程中工作都不正常!
那么,該如何讓他正常工作呢?很簡單,做完事情之前,讓線程不要結束,保持空轉狀態就行了。
- (void)threadFunc
{
NSAutoReleasePool *pool = [NSAutoReleasePool new];
//do something
……
//send your request
[NSURLConnection connectionWithRequest:xxx];
//do some other thing
……
while(shouldExit)
{
[[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];
}
[pool release];
}