RunLoop這個傢伙在iOS開發中,咱們一直在用,卻從未注意過他,甚至都不從見過他的面孔,那個這個神祕的傢伙到底是作什麼的?首先,咱們先來觀察一下咱們的程序運行機制。安全
不管是面向對象的語言或是面向過程的語言,代碼的執行終究是面向過程的。線程也同樣,一個線程從開始代碼執行,到結束代碼銷燬。就像HELLO WORLD程序,打印出字符串後程序就結束了,那麼,咱們的app是如何實現以下這樣的機制的呢:app從運行開始一直處於待命狀態,接收到相似點擊事件等用戶交互後執行相應操做,完成後繼續等待交互響應,直到咱們將程序殺死。經過這個過程的分析,咱們可能會猜到,咱們執行的主線程必定是在一個死循環中,沒有任務的時候進行休眠,接收到任務後被激活執行任務。如今咱們能夠理解了,這樣一個管理線程執行任務的機制就是RunLoop機制,線程在執行中的休眠與激活就是由RunLoop對象進行管理的。app
上面咱們說到,RunLoop是用來管理線程的,那麼他們直接有着怎樣的關係,又是怎樣進行交互的呢。事實上,每個線程中都有一個Runloop對象,能夠經過具體方法得到。這裏有一點須要咱們注意,官方文檔上描述,雖然每個線程中均可以獲取RunLoop對象,可是並非每個線程中都有這個實例對象,咱們能夠這樣理解:若是咱們不獲取runloop,這個runloop就不存在,咱們獲取時,若是不存在,就會去建立。在主線程中,這個MainRunLoop是默認建立並運行激活的。框架
NSRunLoop是Cocoa框架中的類,與之對應,在Core Fundation中是CFRunLoopRef類。這二者的區別是前者不是線程安全的,然後者是線程安全的。咱們這裏只來討論NSRunLoop的屬性和方法:async
+ (NSRunLoop *)currentRunLoop;函數
獲取當前線程的RunLoop:有則獲取,無則建立oop
+ (NSRunLoop *)mainRunLoop ;學習
獲取主線程的RunLoopspa
@property (readonly, copy) NSString *currentMode;.net
獲取當前runloop的執行模式,兩種模式以下:線程
NSString * const NSDefaultRunLoopMode;
默認模式,接收大部分輸入源的響應
NSString * const NSRunLoopCommonModes;
多種模式的集合
- (CFRunLoopRef)getCFRunLoop;
獲取RunLoop的CFRunLoopRef對象
- (void)addTimer:(NSTimer *)timer forMode:(NSString *)mode;
將定時器添加到runloop中
- (void)addPort:(NSPort *)aPort forMode:(NSString *)mode;
添加輸入源端口到runloop中,NSPort對象能夠理解爲詳細的載體,會傳遞消息與其代理。
- (void)removePort:(NSPort *)aPort forMode:(NSString *)mode;
將某個輸入源端口移除
- (NSDate *)limitDateForMode:(NSString *)mode;
獲取下個響應時間
解釋:例如定時器的執行,其並非按時間的間隔進行調用方法,而是在定時器註冊到runloop中後,runloop會設置一個一個的時間點進行調用,好比10,20,30。若是錯過了某個時間點,定時器並不會延時調用,而是直接等待下一個時間點調用,因此定時器並非精準的。
- (void)acceptInputForMode:(NSString *)mode beforeDate:(NSDate *)limitDate;
在某個時間期限前接收響應
- (void)run;
開始運行
- (void)runUntilDate:(NSDate *)limitDate;
到某個時間點運行
- (BOOL)runMode:(NSString *)mode beforeDate:(NSDate *)limitDate;
在某個期限前運行
正如前面所說,咱們一直在使用他,卻不多見到他。而且,咱們在大多數狀況下,都不須要顯式的建立或者啓動RunLoop,有兩種狀況,咱們卻必須手動設置它:
定時器的實現即是基於runloop的,平時咱們使用定時器你或許並無對runloop作什麼操做,那是由於主線程的runloop默認是開啓運行的,若是咱們在分線程中也須要重複執行某一動做,以下:
- (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view, typically from a nib. queue = dispatch_queue_create("myQueue", DISPATCH_QUEUE_CONCURRENT); dispatch_async(queue, ^{ NSTimer * timer = [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:@selector(time) userInfo:nil repeats:YES]; }); } -(void)time{ NSLog(@"run"); }
你會發現,程序運行後並無打印任何信息,方法並無被調用,咱們必須在線程中手動的執行以下代碼:
[[NSRunLoop currentRunLoop] run];
定時器才能正常工做。
某些延時函數和選擇器在分線程中的使用,咱們也必須手動開啓runloop,這些方法以下:
@interface NSObject (NSDelayedPerforming)
- (void)performSelector:(SEL)aSelector withObject:(id)anArgument afterDelay:(NSTimeInterval)delay inModes:(NSArray *)modes;
- (void)performSelector:(SEL)aSelector withObject:(id)anArgument afterDelay:(NSTimeInterval)delay;
+ (void)cancelPreviousPerformRequestsWithTarget:(id)aTarget selector:(SEL)aSelector object:(id)anArgument;
+ (void)cancelPreviousPerformRequestsWithTarget:(id)aTarget;
- (void)performSelector:(SEL)aSelector target:(id)target argument:(id)arg order:(NSUInteger)order modes:(NSArray *)modes;
- (void)cancelPerformSelector:(SEL)aSelector target:(id)target argument:(id)arg;
- (void)cancelPerformSelectorsWithTarget:(id)target;
RunLoop更強大的地方在於對消息的監聽,由於CFRunLoopRef的線程安全優點,咱們一般會更多使用後者。
細心的你可能會發現,輸入源被註冊進Runloop中時會有方法進行remove,可是定時器卻沒有,可是定時器中的invalidate方法能夠將其從runloop中移除,正如官方文檔的說明:invalidate是重要也是惟一的能夠將定時器從runloop的註銷的方法,因此若是咱們建立了定時器,就必定要在不使用時調用invalidate方法。我不知道apple爲什麼將定時器的方法分離開來,可能的緣由是讓開發者更少的顯式調用runloop的方法,你如果知道緣由,懇請留言指導。
關於定時器的問題,在另外一篇博客中有介紹:http://my.oschina.net/u/2340880/blog/398598。
學習使用 歡迎轉載
疏漏之處 歡迎指正
專一技術,熱愛生活,交流技術,也作朋友。
——琿少 QQ羣:203317592