天天看點

NSRunLoop的進一步了解 NSRunLoop的進一步了解

NSRunLoop的進一步了解

2011-8-22 15:42| 釋出者: Vincent| 檢視: 716| 評論: 17|原作者: iGoogle

摘要: 本文基于一篇網絡文章,加入了一些自己的了解,希望對大家有所幫助 iPhone應用開發中關于NSRunLoop的概述是本文要介紹的内容,NSRunLoop是一種更加高明的消息處理模式,他就高明在對消息處理過程進行了更好的抽象和 ...

本文基于一篇網絡文章,加入了一些自己的了解,希望對大家有所幫助

iPhone應用開發中關于NSRunLoop的概述是本文要介紹的内容,NSRunLoop是一種更加高明的消息處理模式,他就高明在對消息處理過程進行了更好的抽象和封裝,這樣才能是的你不用處理一些很瑣碎很低層次的具體消息的處理,在NSRunLoop中每一個消息就被打包在input source或者是timer source中了,來看詳細内容。

1.什麼是NSRunLoop

我們會經常看到這樣的代碼:

  1. - (IBAction)start:(id)sender  
  2. {  
  3. pageStillLoading = YES;  
  4. [NSThread detachNewThreadSelector:@selector(loadPageInBackground:)toTarget:self withObject:nil];  
  5. [progress setHidden:NO];  
  6. while (pageStillLoading) {  
  7. [NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];  
  8. }  
  9. [progress setHidden:YES];  
  10. }  

複制代碼 這段代碼很神奇的,因為他會“暫停”代碼運作,而且程式運作不會因為這裡有一個while循環而受到影響。在[progress setHidden:NO]執行之後,整個函數想暫停了一樣停在循環裡面,等loadPageInBackground裡面的操作都完成了以後才讓[progress setHidden:YES]運作。這樣做就顯得簡介,而且邏輯很清晰。如果你不這樣做,你就需要在loadPageInBackground裡面表示load完成的地方調用[progress setHidden:YES],顯得代碼不緊湊而且容易出錯。

[iGoogle有話說:應用程式架構主線程已經封裝了對NSRunLoop runMode:beforeDate:的調用;它和while循環構成了一個消息泵,不斷擷取和處理消息;可能大家會比較奇怪,既然主線程中已經封裝好了對NSRunLoop的調用,為什麼這裡還可以再次調用,這個就是它與Windows消息循環的差別,它可以嵌套調用.當再次調用while+NSRunLoop時候程式并沒有停止執行,它還在不停提取消息/處理消息.這一點與Symbian中Active Scheduler的嵌套調用達到同步作用原理是一樣的.]

那麼具體什麼是NSRunLoop呢?其實NSRunLoop的本質是一個消息機制的處理模式。如果你對vc++程式設計有一定了解,在windows中,有一系列很重要的函數SendMessage,PostMessage,GetMessage,這些都是有關消息傳遞處理的API。

但是在你進入到Cocoa的程式設計世界裡面,我不知道你是不是走的太快太匆忙而忽視了這個很重要的問題,Cocoa裡面就沒有提及到任何關于消息處理的API,開發者從來也沒有自己去關心過消息的傳遞過程,好像一切都是那麼自然,像大自然一樣自然?在Cocoa裡面你再也不用去自己定義WM_COMMAD_XXX這樣的宏來辨別某個消息,也不用在switch-case裡面去對特定的消息做特别的處理。難道是Cocoa裡面就沒有了消息機制?答案是否定的,隻是Apple在設計消息處理的時候采用了一個更加高明的模式,那就是RunLoop。

2. NSRunLoop工作原理

接下來看一下NSRunLoop具體的工作原理,首先是官方文檔提供的說法,看圖:

NSRunLoop的進一步了解 NSRunLoop的進一步了解

通過所有的“消息”都被添加到了NSRunLoop中去,而在這裡這些消息并分為“input source”和“Timer source” 并在循環中檢查是不是有事件需要發生,如果需要那麼就調用相應的函數處理。為了更清晰的解釋,我們來對比VC++和iOS消息處理過程。

VC++中在一切初始化都完成之後程式就開始這樣一個循環了(代碼是從戶sir mfc程式設計課程的slides中截取):

  1. int APIENTRY WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,LPSTR  lpCmdLine,int nCmdShow){  
  2. ...  
  3. while (GetMessage(&msg, NULL, 0, 0)){  
  4. if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg)){  
  5. TranslateMessage(&msg);  
  6. DispatchMessage(&msg);  
  7. }  
  8. }  
  9. }  
複制代碼 可以看到在GetMessage之後就去分發處理消息了,而iOS中main函數中隻是調用了UIApplicationMain,那麼我們可以介意猜出UIApplicationMain在初始化完成之後就會進入這樣一個情形:
  1. int UIApplicationMain(...){  
  2. ...  
  3. while(running){  
  4. [NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];  
  5. }  
  6. ...  
  7. }

複制代碼 是以在UIApplicationMain中也是同樣在不斷處理runloop才是的程式沒有退出。剛才的我說了NSRunLoop是一種更加高明的消息處理模式,他就高明在對消息處理過程進行了更好的抽象和封裝,這樣才能是的你不用處理一些很瑣碎很低層次的具體消息的處理,在NSRunLoop中每一個消息就被打包在input source或者是timer source中了,當需要處理的時候就直接調用其中包含的相應對象的處理函數了。

是以對外部的開發人員來講,你感受到的就是,把source/timer加入到runloop中,然後在适當的時候類似于[receiver action]這樣的事情發生了。甚至很多時候,你都沒有感受到整個過程前半部分,你隻是感覺到了你的某個對象的某個函數調用了。

比如在UIView被觸摸時會用touchesBegan/touchesMoved等等函數被調用,也許你會想,“該死的,我都不知道在那裡被告知有觸摸消息,這些處理函數就被調用了!?”是以,消息是有的,隻是runloop已經幫你做了!為了證明我的觀點,我截取了一張debug touchesBegan的call stack,有圖有真相,如圖:

NSRunLoop的進一步了解 NSRunLoop的進一步了解
現在會過頭來看看剛才的那個會“暫停”代碼的例子,有沒有更加深入的認識了呢?

收藏 分享 發表評論

最新評論

g3ee  2011-9-7 18:13
rongjianxing 發表于 2011-9-7 17:31 
NSRunLoop的進一步了解 NSRunLoop的進一步了解
”比如在UIView被觸摸時會用touchesBegan/touchesMoved等等函數被調用,也許你會想,“該死的,我都不知道在 ...
頂牛人
rongjianxing  2011-9-7 17:31

”比如在UIView被觸摸時會用touchesBegan/touchesMoved等等函數被調用,也許你會想,“該死的,我都不知道在那裡被告知有觸摸消息,這些處理函數就被調用了!“

在win中,如果使用純api手工打造,的确沒有這功能。

但使用mfc,wtl等類庫,OnCreate()等函數也是一樣”都不知道在那裡被告知有消息,這些處理函數就被調用了“

rongjianxing  2011-9-7 17:28

",Cocoa裡面就沒有提及到任何關于消息處理的API,開發者從來也沒有自己去關心過消息的傳遞過程,好像一切都是那麼自然,像大自然一樣自然?在Cocoa裡面你再也不用去自己定義WM_COMMAD_XXX這樣的宏來辨別某個消息,也不用在switch-case裡面去對特定的消息做特别的處理。"

這個說法也有問題,cocoa與win的開發者都要關心消息的處理,隻是在windows下拉使用api時,要做WM_COMMAD_XXX和消息函數對應起來,而cocoa是實作特定接口的delegate與引起消息的源對應起來。

而通過使用ide,都可以通過拉拖點選來完成對應操作。

rongjianxing  2011-9-7 17:13

“可能大家會比較奇怪,既然主線程中已經封裝好了對NSRunLoop的調用,為什麼這裡還可以再次調用,這個就是 它與Windows消息循環的差別,它可以嵌套調用.”

這句不準确,windows的消息循環也可以嵌套調用,比如win處理拖動選擇時可以這樣實作:

  1. case WM_LBUTTONDOWN:
  2. {
  3.         GetCapture();
  4.         while (GetMessage (&msg, NULL, 0, 0)) // -------開始選擇------
  5.         {
  6.                 if (WM_MOUSEMOVE == msg.message)
  7.                 {
  8.                         // 畫選擇框
  9.                 }
  10.                 if (WM_LBUTTONUP == msg.message)
  11.                 {                        
  12.                         // 畫選擇框
  13.                         ReleaseCapture ();
  14.                         break; // 跳出循環 
  15.                 }
  16.         } // while 結束.  -------結束選擇-------
  17. }
複制代碼
bishop  2011-9-7 11:45
mark    學習了
pxfokko  2011-9-6 16:39
學習了 
NSRunLoop的進一步了解 NSRunLoop的進一步了解
NSRunLoop的進一步了解 NSRunLoop的進一步了解
NSRunLoop的進一步了解 NSRunLoop的進一步了解
ericni  2011-9-1 11:06
有點懂了,多謝
snakeninny  2011-8-26 10:56
支援一下!小弟還看不懂……
iGoogle  2011-8-26 10:12
happyboyxq 發表于 2011-8-26 10:01 
NSRunLoop的進一步了解 NSRunLoop的進一步了解
我還有點不明白,從這個方法來看,調用start函數的輸入源(暫稱為輸入源1),此時輸入源1應該是擁有運作循環 ...

他們是不同的線程,

新啟動的線程(稱為線程1)根本不在主線程(我們姑且稱start函數執行的線程為主線程)中執行。

主線程和線程1的切換執行是cpu排程的事

這裡的NSRunLoop是主線程中的消息循環機制,每個線程都有自己的NSRunLoop

happyboyxq  2011-8-26 10:01

我還有點不明白,從這個方法來看,調用start函數的輸入源(暫稱為輸入源1),此時輸入源1應該是擁有運作循環的控制權的(文檔上說運作循環會将控制權交給使用者的)

- (IBAction)start:(id)sender  

{  

pageStillLoading = YES;  

此處又建立了一個線程,從代碼會在下面的運作循環處停止可知,該線程為輸入源2,那何時這個輸入源2會擁有運作循環的控制權并執行函數呢?此時的輸入源1并未執行完成,應該不會歸還控制權給運作循環才對啊。這樣不是陷入了一個循環嗎,輸入源1未交出控制權,輸入源2在等待控制權,那此時程式是怎麼執行到最後的呢,pageStillLoading設為NO應該是在輸入源2裡面做的。這表示中間應該有某個過程把輸入源1的控制權交給了輸入源2,我就是想請教一下這個過程是在哪,什麼時候執行的。

[NSThread detachNewThreadSelector:@selector(loadPageInBackground:)toTarget:self withObject:nil];  

[progress setHidden:NO];  

while (pageStillLoading) {  

此處調用這個方法是否就是輸入源1為了交出控制權呢?這樣輸入源2就有機會執行,待執行完成後退出,pageStillLoading=NO,因為輸入源1仍存在于運作循環中,是以繼續執行,這樣後面的代碼才得以執行呢?

[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];  

}  

[progress setHidden:YES];  

}  

cheungching  2011-8-25 20:45
為什麼在UIApplicationDelegate中做這個會出現一會黑屏呢?!
cheungching  2011-8-25 20:04
呵呵,分析的太好了。
dymx101  2011-8-24 17:17
學習了
NSRunLoop的進一步了解 NSRunLoop的進一步了解
iGoogle  2011-8-24 11:57
  1. [NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];  
複制代碼 相當于:
  1. TranslateMessage(&msg);  
  2. DispatchMessage(&msg);  
複制代碼 是以即使嵌套使用,它仍舊在不停處理消息,不會讓程式停止運作,界面更不會失去響應
iGoogle  2011-8-24 11:55
happyboyxq 發表于 2011-8-24 09:47 
NSRunLoop的進一步了解 NSRunLoop的進一步了解

while (pageStillLoading) {  

[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NS ...

不是一直等待,實際上它還在處理消息,界面不會停止響應,

它的作用是同步,隻是不執行while循環後面跟着的語句.

happyboyxq  2011-8-24 09:47

while (pageStillLoading) {  

[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];  

}  

裡的NSRunLoop應該是主線程的運作循環吧,這樣主線程不是一直處于等待狀态?如果我想做點别的什麼怎麼辦呢?再啟一個線程?

pp_psy  2011-8-22 20:08
頂,多謝iGoogle哥

檢視全部評論(17)

繼續閱讀