版本記錄
| 版本號 | 時(shí)間 |
|---|---|
| V1.0 | 2017.08.22 |
前言
NSRunloop是OC Foundation框架中非常重要的一個(gè)類,很多時(shí)候我們會使用它,但是未必對其有深入的了解,接下來幾篇我就會帶著大家重新學(xué)習(xí)一下NSRunloop這個(gè)類,從簡單到復(fù)雜,從基本到深化,我會一步步的走完。希望對大家有所幫助。具體可以參考蘋果的開發(fā)文檔。
NSRunloop基本了解
Runloop即運(yùn)行循環(huán)。NSRunloop是對CFRunloop的封裝,為什么你的APP放在那里不去動它,在某個(gè)時(shí)間點(diǎn)去操作它,它還會給你反饋。就是因?yàn)镽unloop的存在,因?yàn)镽unloop的存在,保證你的程序不會死。具體可以參見蘋果開發(fā)文檔。也可以在xcode里面下載。具體可參照下圖。

安裝好了以后大家可以從下面的路徑/Applications/Xcode.app/Contents/Developer/Documentation/DocSets查看,具體如下圖所示。

至于開發(fā)文檔的使用后面會單獨(dú)抽出來一篇和大家詳細(xì)說明。
使用command + shift + 0快捷鍵出來的文檔,大家也可以參考。
NSRunloop的本質(zhì)
NSRunloop是對CFRunloop的封裝。

NSRunloop主要作用
NSRunloop主要有以下作用:
- 使程序一直運(yùn)行并接受用戶輸入
- 決定程序在何時(shí)處理一些Event
- 調(diào)用解耦
(Message Queue) - 節(jié)省
CPU時(shí)間(沒事的時(shí)候閑著,有事的時(shí)候處理)
依賴NSRunloop的類和框架
NSTimerUIEventautoreleaseNSObject(NSDelaydPerforming)NSObject(NSThreadPerformAddtion)CADisplayLinkCATransitionCAAnimationdispatch_get_main_queue()
NSRunloop消息類型
下面我們看一下消息類型,其實(shí)就是很經(jīng)典那個(gè)圖。

Port:
監(jiān)聽程序的Mach ports,Mach ports是一個(gè)比較底層的東西,可以簡單的理解為:內(nèi)核通過port這種方式將信息發(fā)送,而mach則監(jiān)聽內(nèi)核發(fā)來的port信息,然后將其整理,打包發(fā)給runloop。-
Customer:
很明顯,由開發(fā)人員自己發(fā)送。不僅僅是發(fā)送,過程的話相當(dāng)復(fù)雜,蘋果也提供了一個(gè)CFRunLoopSource來幫助處理。由于很少用到,可以簡單說下核心,但是對幫助我們理解runloop卻很有幫助:- 定義輸入源(數(shù)據(jù)結(jié)構(gòu))
- 將輸入源添加到runloop,那么這樣就有了接受者,即為R1。
- 協(xié)調(diào)輸入源的客戶端(單獨(dú)線程),專門監(jiān)聽消息,然后將消息打包成runloop能夠處理的樣式,即第一步定義的輸入源。它類似Mach的功能。
- 誰來發(fā)送消息的問題?上面的machport是由內(nèi)核發(fā)送的。自定義的當(dāng)然要我們自己發(fā)送了。。。首先必須是另一個(gè)線程來發(fā)送(當(dāng)然如果只是測試的話可以和第三步在同一個(gè)線程),先發(fā)送消息給輸入源,然后喚醒R1,因?yàn)镽1一般處于休眠狀態(tài),然后R1根據(jù)輸入源來做相應(yīng)的處理。
Selector Sources
NSObject類提供了很多方法供我們使用,這些方法是添加到runloop的,所以如果沒有開啟runloop的話,不會運(yùn)行。Timer Sources:它的事件發(fā)送是同步的,這個(gè)用的比較多。-
Observers,觀察者:首先它并不屬于事件源(不會影響runloop的生命周期),它比較特殊,用于觀察runloop自身的一些狀態(tài)的,有以下幾種:- 進(jìn)入runloop
- runloop即將執(zhí)行定時(shí)器
- runloop即將執(zhí)行輸入源(Port,Customer,Selector Sources)
- runloop即將休眠
- runloop被喚醒,在處理完喚醒它的事件之前
- 退出
NSRunloop API文檔
下面我們就看一下蘋果給我們預(yù)留的API文檔。
#import <Foundation/NSObject.h>
#import <Foundation/NSDate.h>
#import <CoreFoundation/CFRunLoop.h>
@class NSTimer, NSPort, NSArray<ObjectType>, NSString;
NS_ASSUME_NONNULL_BEGIN
FOUNDATION_EXPORT NSRunLoopMode const NSDefaultRunLoopMode;
FOUNDATION_EXPORT NSRunLoopMode const NSRunLoopCommonModes NS_AVAILABLE(10_5, 2_0);
//這里是NSRunLoop本類
@interface NSRunLoop : NSObject {
@private
id _rl;
id _dperf;
id _perft;
id _info;
id _ports;
void *_reserved[6];
}
#if FOUNDATION_SWIFT_SDK_EPOCH_AT_LEAST(8)
@property (class, readonly, strong) NSRunLoop *currentRunLoop;
@property (class, readonly, strong) NSRunLoop *mainRunLoop NS_AVAILABLE(10_5, 2_0);
#endif
@property (nullable, readonly, copy) NSRunLoopMode currentMode;
- (CFRunLoopRef)getCFRunLoop CF_RETURNS_NOT_RETAINED;
- (void)addTimer:(NSTimer *)timer forMode:(NSRunLoopMode)mode;
- (void)addPort:(NSPort *)aPort forMode:(NSRunLoopMode)mode;
- (void)removePort:(NSPort *)aPort forMode:(NSRunLoopMode)mode;
- (nullable NSDate *)limitDateForMode:(NSRunLoopMode)mode;
- (void)acceptInputForMode:(NSRunLoopMode)mode beforeDate:(NSDate *)limitDate;
@end
//這里是NSRunLoop其中的一個(gè)分類NSRunLoopConveniences
@interface NSRunLoop (NSRunLoopConveniences)
- (void)run;
- (void)runUntilDate:(NSDate *)limitDate;
- (BOOL)runMode:(NSRunLoopMode)mode beforeDate:(NSDate *)limitDate;
#if (TARGET_OS_MAC && !(TARGET_OS_EMBEDDED || TARGET_OS_IPHONE))
- (void)configureAsServer NS_DEPRECATED(10_0, 10_5, 2_0, 2_0);
#endif
/// Schedules the execution of a block on the target run loop in given modes.
/// - parameter: modes An array of input modes for which the block may be executed.
/// - parameter: block The block to execute
- (void)performInModes:(NSArray<NSRunLoopMode> *)modes block:(void (^)(void))block API_AVAILABLE(macosx(10.12), ios(10.0), watchos(3.0), tvos(10.0));
/// Schedules the execution of a block on the target run loop.
/// - parameter: block The block to execute
- (void)performBlock:(void (^)(void))block API_AVAILABLE(macosx(10.12), ios(10.0), watchos(3.0), tvos(10.0));
@end
/**************** Delayed perform ******************/
@interface NSObject (NSDelayedPerforming)
- (void)performSelector:(SEL)aSelector withObject:(nullable id)anArgument afterDelay:(NSTimeInterval)delay inModes:(NSArray<NSRunLoopMode> *)modes;
- (void)performSelector:(SEL)aSelector withObject:(nullable id)anArgument afterDelay:(NSTimeInterval)delay;
+ (void)cancelPreviousPerformRequestsWithTarget:(id)aTarget selector:(SEL)aSelector object:(nullable id)anArgument;
+ (void)cancelPreviousPerformRequestsWithTarget:(id)aTarget;
@end
//這里是NSRunLoop其中的一個(gè)分類NSOrderedPerform
@interface NSRunLoop (NSOrderedPerform)
- (void)performSelector:(SEL)aSelector target:(id)target argument:(nullable id)arg order:(NSUInteger)order modes:(NSArray<NSRunLoopMode> *)modes;
- (void)cancelPerformSelector:(SEL)aSelector target:(id)target argument:(nullable id)arg;
- (void)cancelPerformSelectorsWithTarget:(id)target;
@end
從這個(gè)API文檔上我們可以看見,提供的是一個(gè)本類,兩個(gè)分類(NSRunLoopConveniences 和 NSOrderedPerform)。下面以表格的形式給出。
| 模塊 | 內(nèi)容 |
|---|---|
| 獲取Runloop及其模式 | @property(class, readonly, strong) NSRunLoop *currentRunLoop; |
| @property(readonly, copy) NSRunLoopMode currentMode; | |
| - (NSDate *)limitDateForMode:(NSRunLoopMode)mode; | |
| @property(class, readonly, strong) NSRunLoop *mainRunLoop; | |
| - (CFRunLoopRef)getCFRunLoop; | |
| 定時(shí)器管理 | - (void)addTimer:(NSTimer *)timer forMode:(NSRunLoopMode)mode; |
| 端口Ports管理 | - (void)addPort:(NSPort *)aPort forMode:(NSRunLoopMode)mode; |
| - (void)removePort:(NSPort *)aPort forMode:(NSRunLoopMode)mode; | |
| configureAsServer | - (void)configureAsServer; |
| Running a loop | - (void)run; |
| - (BOOL)runMode:(NSRunLoopMode)mode beforeDate:(NSDate *)limitDate; | |
| - (void)runUntilDate:(NSDate *)limitDate; | |
| - (void)acceptInputForMode:(NSRunLoopMode)mode beforeDate:(NSDate *)limitDate; | |
| scheduling and canceling Messages | - (void)performSelector:(SEL)aSelector target:(id)target argument:(id)arg order:(NSUInteger)order modes:(NSArray<NSRunLoopMode> *)modes; |
| - (void)cancelPerformSelector:(SEL)aSelector target:(id)target argument:(id)arg; | |
| - (void)cancelPerformSelectorsWithTarget:(id)target; | |
| Run Loop Modes | - (void)performBlock:(void (^)(void))block; |
| - (void)performInModes:(NSArray<NSRunLoopMode> *)modes block:(void (^)(void))block; |
下面我們就看一下文檔里面給出的NSRunloop主要的方法和屬性等信息。

參考文章
1. iOS NSRunloop詳解
2. NSRunLoop原理詳解——不再有盲點(diǎn)
后記
未完,待續(xù)~~~
