標籤:
一、淺識RunLoop
RunLoop在開發中我們一直在用,但是沒有注意他。要想理解RunLoop,首先我們需要先瞭解一下程式運行機制。
程式運行機制:我們都知道OC是運行時語言,也就是說對象的類型是在程式啟動並執行時候確定的。並調用類與對象相應的方法。但是最終代碼的執行始終是面向過程的。線程也是一樣:一個線程從開始代碼執行,到結束代碼銷毀。app如何?這樣的機制:app從運行開始一直處於待命狀態,接收到事件之後執行操作,操作完成後繼續等待相應,直到程式終止運行。這樣的管理線程執行任務的機制就是RunLoop機制。線程在執行中的休眠和啟用就是由RunLoop對象進行管理的。
二、RunLoop與線程的關係
RunLoop是用來管理線程的。每一個線程都有一個RunLoop對象。可以通過具體的方法去獲得。但是需要注意:雖然每一個線程都可以擷取RUnLoop對象,但是並不是每一個線程中都有執行個體對象,我們可以這樣理解:如果我們不擷取RunLoop,這個RunLoop就不存在,我們擷取時,如果不存在,就會去建立。在主線程中,這個MainRunLoop是預設建立並運行啟用的。
三、NSRunLoop
NSRunLoop是Cocoa架構中的類,與之對應的是在Core Fundation中有一個CFRunLoopRef類。這兩者的區別是前者不是安全執行緒的,而CFRunLoopRef是安全執行緒的。
1、擷取主線程的NSRunLoop
//擷取主線程的NSRunLoop+(NSRunLoop *)mainRunLoop;
2、擷取當前線程的NSRunLoop
//擷取當前線程的RunLoop:有的話就直接擷取,沒有的話就自動建立+(NSRunLoop *)currentRunLoop;
3、NSRunLoop的執行模式
//擷取當前runloop的執行模式@property(readonly,copy)NSString *currentMode;//兩種執行模式//預設的模式,接收大部分輸入源的響應NSString *const NSDefaultRunLoopMode;//多種模式的集合NSString *const NSRunLoopCommonModes;
4、擷取RunLoop的CFRunLoopRef對象
//擷取RunLoop的CFRunLoopRef對象-(CFRunLoopREF)getCFRunLoop;
5、將定時器添加到RunLoop中
//將定時器添加到runloop中-(void)addTimer:(NSTimer *)timer forMode:(NSString *)mode;
6、擷取下個回應時間
-(NSDate *)limitDateForMode:(NSString *)mode;
定時器的執行,其實並不是按照時間段額間隔進行調用方法,而是在定時器註冊到RunLoop中後,RunLoop會設定一個一個的時間點進行調用,例如,5,10,15,20等等。如果錯過了某個時間點,定時器並不會延遲調用,而是直接等待下一個時間點調用,所以定時器並不是準確的。
7、在某個時間期限前接收相應
-(void)aceptInputForMode:(NSString *)mode beforeDate:(NSDate *)limitDate;
8、開始運行
-(void)run;
9、到某個時間點運行
-(void)runUntilDate:(NSDate *)limitDate;
10、在某個期限前運行
-(BOOL)runMode:(NSString *)mode beforeDate:(NSDate *)limitDate;
11、
- (void)addPort:(NSPort *)aPort forMode:(NSString *)mode;添加輸入源連接埠到runloop中,NSPort對象可以理解為詳細的載體,會傳遞訊息與其代理。- (void)removePort:(NSPort *)aPort forMode:(NSString *)mode;將某個輸入源連接埠移除
四、RunLoop的調用
一般情況下我們很少去顯式調用或者啟動RunLoop,但是下邊的情況需要手動設定。
1、在分線程中使用定時器
定時器的實現是基於RunLoop的,平時我們使用定時器或許並沒有對RunLoop做什麼操作,那是因為主線程的RunLoop預設是開啟啟動並執行,如果我們進行如下操作:
-(void)viewDidLoad{ [super viewDidLoad]; dispatch_queue_t queue = dispatch_queue_create("myQueue",DISPATCH_QUEUE_CONCURRENT); dispatch_async(queue,^{ NSTimer *timer = [NSTimer scheduledTimerWithTimerInterval:1 target:self selector:@selector(time) userInfo:nil repeats:YES];}); }-(void)time{NSLog(@"runTimer");}
此時運行,控制台不會輸出runTimer。我們必須線上程中手動的執行如下代碼:
[[NSRunLoop currentRunLoop] run];
這樣定時器才可以正常工作。
2、當線程中使用如下的方法時
某些延遲函數和選取器在分線程中的使用,我們必須手動開始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中時會有方法進行remove。但是定時器沒有remove,但是它的invalidate方法可以將其從RunLoop中移除。invalidate是重要的也是唯一的將定時器從RunLoop中登出的方法,所以如果我們建立了定時器,就一定要再不適用的時候調用invalidate方法。
六、附
參考自:http://my.oschina.net/u/2340880/blog/491966
RunLoop機制理解