NSRunloop簡單細(xì)說(一)—— 整體了解

版本記錄

版本號 時(shí)間
V1.0 2017.08.22

前言

NSRunloopOC 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里面下載。具體可參照下圖。

開發(fā)文檔下載

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

文檔路徑

至于開發(fā)文檔的使用后面會單獨(dú)抽出來一篇和大家詳細(xì)說明。

使用command + shift + 0快捷鍵出來的文檔,大家也可以參考。

NSRunloop的本質(zhì)

NSRunloop是對CFRunloop的封裝。

構(gòu)成元素

NSRunloop主要作用

NSRunloop主要有以下作用:

  • 使程序一直運(yùn)行并接受用戶輸入
  • 決定程序在何時(shí)處理一些Event
  • 調(diào)用解耦(Message Queue)
  • 節(jié)省CPU時(shí)間(沒事的時(shí)候閑著,有事的時(shí)候處理)

依賴NSRunloop的類和框架

  • NSTimer
  • UIEvent
  • autorelease
  • NSObject(NSDelaydPerforming)
  • NSObject(NSThreadPerformAddtion)
  • CADisplayLink
  • CATransition
  • CAAnimation
  • dispatch_get_main_queue()

NSRunloop消息類型

下面我們看一下消息類型,其實(shí)就是很經(jīng)典那個(gè)圖。

NSRunloop消息類型
  • 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è)分類(NSRunLoopConveniencesNSOrderedPerform)。下面以表格的形式給出。

模塊 內(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主要的方法和屬性等信息。

NSRunloop主要的方法和屬性

參考文章

1. iOS NSRunloop詳解
2. NSRunLoop原理詳解——不再有盲點(diǎn)

后記

未完,待續(xù)~~~

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容