就個人經(jīng)驗而言,在iphone線程中使用異步NSURLConnection的經(jīng)驗可以說是一個完全和愉悅搭不上邊的事情。他給我?guī)淼穆闊┛烧娌簧佟@纾皫滋?,幫客戶定位一個問題的時候發(fā)生的事情。
事情經(jīng)過是這樣的:客戶反饋,無法正常使用我們提供的某個和網(wǎng)絡(luò)相關(guān)的功能,網(wǎng)絡(luò)回調(diào)沒有收到。但是其他回調(diào)可以正常工作,并且所有回調(diào)都是以同樣的邏輯放在某個地方的。
我先確認(rèn)了他的使用方式是否正確,并確認(rèn)了輸入?yún)?shù)的正確性,并且驗證了回調(diào)的正確設(shè)置以及回調(diào)函數(shù)的使用無誤。一切都沒有問題,但是就是無法收到回調(diào)。
一切都那么的神奇,功能的調(diào)用并沒有什么特殊的,但是就單單這個功能工作不正常。中間又經(jīng)過一些確認(rèn),發(fā)現(xiàn)數(shù)據(jù)并沒有到達服務(wù)端。但是同樣的使用方式在我們自己的環(huán)境下工作又是正常的。
花了將近5個小時,還是沒有發(fā)現(xiàn)原因,就在我準(zhǔn)備放棄的時候,突然想到,莫非他的調(diào)用位置是線程中調(diào)用?經(jīng)過確認(rèn),發(fā)現(xiàn)果然是在線程中調(diào)用的!讓他們使用performOnMainThread的方式調(diào)用,終于解決了問題。
以前遇到過一次在線程中調(diào)用異步網(wǎng)絡(luò)的情況,請教同事,同事告知,異步網(wǎng)絡(luò)需要自己的RunLoop,所以要在線程中使用異步網(wǎng)絡(luò)必須有自己的RunLoop,可以將他放到主線程的RunLoop。
但是,這樣子,多線程的性能必然被降低,因為這樣網(wǎng)絡(luò)的工作就都是在主線程中完成的。同時懷疑,這樣子的網(wǎng)絡(luò)性能設(shè)計不是很低下!
剛好有點時間,就仔細(xì)的翻了一下蘋果的開發(fā)文檔,發(fā)現(xiàn)其中指出,所有的NSThread都有一個屬于自己的NSRunLoop,而NSURLConnection的回調(diào)都會回調(diào)到當(dāng)前線程中!
這個和我目前遇到的完全不一樣!我無法在當(dāng)前線程中收到回調(diào)!
又仔細(xì)在網(wǎng)絡(luò)上搜索之后發(fā)現(xiàn),原來,是因為網(wǎng)絡(luò)回調(diào)的時候線程的NSRunLoop已經(jīng)被無效的原因。
我們常見的線程中網(wǎng)絡(luò)調(diào)用是這樣的:
- (void)threadFunc
{
NSAutoReleasePool *pool = [NSAutoReleasePool new];
//do something
……
//send your request
[NSURLConnection connectionWithRequest:xxx];
//do some other thing
……
[pool release];
}
一般來說,這個函數(shù)會很快走完,線程就結(jié)束了。所以,當(dāng)網(wǎng)絡(luò)響應(yīng)回來的時候,你的線程已經(jīng)結(jié)束了。你的網(wǎng)絡(luò)回調(diào)依賴于該線程的NSRunLoop,但是線程已經(jīng)結(jié)束了,所以你的網(wǎng)絡(luò)回調(diào)就無法收到!!同理,所有的performAfterDelay之類的api以及NSTimer的在線程中工作都不正常!
那么,該如何讓他正常工作呢?很簡單,做完事情之前,讓線程不要結(jié)束,保持空轉(zhuǎn)狀態(tài)就行了。
- (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];
}