iOS中RunLoop機制淺探

iOS中RunLoop機制淺探

1、淺識RunLoop

        RunLoop這個傢伙在iOS開發中,咱們一直在用,卻從未注意過他,甚至都不從見過他的面孔,那個這個神祕的傢伙到底是作什麼的?首先,咱們先來觀察一下咱們的程序運行機制。安全

        不管是面向對象的語言或是面向過程的語言,代碼的執行終究是面向過程的。線程也同樣,一個線程從開始代碼執行,到結束代碼銷燬。就像HELLO WORLD程序,打印出字符串後程序就結束了,那麼,咱們的app是如何實現以下這樣的機制的呢:app從運行開始一直處於待命狀態,接收到相似點擊事件等用戶交互後執行相應操做,完成後繼續等待交互響應,直到咱們將程序殺死。經過這個過程的分析,咱們可能會猜到,咱們執行的主線程必定是在一個死循環中,沒有任務的時候進行休眠,接收到任務後被激活執行任務。如今咱們能夠理解了,這樣一個管理線程執行任務的機制就是RunLoop機制,線程在執行中的休眠與激活就是由RunLoop對象進行管理的。app

2、RunLoop與線程的關係

        上面咱們說到,RunLoop是用來管理線程的,那麼他們直接有着怎樣的關係,又是怎樣進行交互的呢。事實上,每個線程中都有一個Runloop對象,能夠經過具體方法得到。這裏有一點須要咱們注意,官方文檔上描述,雖然每個線程中均可以獲取RunLoop對象,可是並非每個線程中都有這個實例對象,咱們能夠這樣理解:若是咱們不獲取runloop,這個runloop就不存在,咱們獲取時,若是不存在,就會去建立。在主線程中,這個MainRunLoop是默認建立並運行激活的。框架

 

3、認識NSRunLoop

        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;

在某個期限前運行

 

4、RunLoop的應用

        正如前面所說,咱們一直在使用他,卻不多見到他。而且,咱們在大多數狀況下,都不須要顯式的建立或者啓動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;

 

5、補充

        RunLoop更強大的地方在於對消息的監聽,由於CFRunLoopRef的線程安全優點,咱們一般會更多使用後者。

        細心的你可能會發現,輸入源被註冊進Runloop中時會有方法進行remove,可是定時器卻沒有,可是定時器中的invalidate方法能夠將其從runloop中移除,正如官方文檔的說明:invalidate是重要也是惟一的能夠將定時器從runloop的註銷的方法,因此若是咱們建立了定時器,就必定要在不使用時調用invalidate方法。我不知道apple爲什麼將定時器的方法分離開來,可能的緣由是讓開發者更少的顯式調用runloop的方法,你如果知道緣由,懇請留言指導。

        關於定時器的問題,在另外一篇博客中有介紹:http://my.oschina.net/u/2340880/blog/398598


 

學習使用 歡迎轉載

疏漏之處 歡迎指正

專一技術,熱愛生活,交流技術,也作朋友。

——琿少 QQ羣:203317592

相關文章
相關標籤/搜索