美文网首页iOS模块详解NSRunLoopiOS技术专题
NSRunloop简单细说(一)—— 整体了解

NSRunloop简单细说(一)—— 整体了解

作者: 刀客传奇 | 来源:发表于2017-08-22 22:35 被阅读164次

    版本记录

    版本号 时间
    V1.0 2017.08.22

    前言

    NSRunloopOC Foundation框架中非常重要的一个类,很多时候我们会使用它,但是未必对其有深入的了解,接下来几篇我就会带着大家重新学习一下NSRunloop这个类,从简单到复杂,从基本到深化,我会一步步的走完。希望对大家有所帮助。具体可以参考苹果的开发文档

    NSRunloop基本了解

    Runloop即运行循环。NSRunloop是对CFRunloop的封装,为什么你的APP放在那里不去动它,在某个时间点去操作它,它还会给你反馈。就是因为Runloop的存在,因为Runloop的存在,保证你的程序不会死。具体可以参见苹果开发文档。也可以在xcode里面下载。具体可参照下图。

    开发文档下载

    安装好了以后大家可以从下面的路径/Applications/Xcode.app/Contents/Developer/Documentation/DocSets查看,具体如下图所示。

    文档路径

    至于开发文档的使用后面会单独抽出来一篇和大家详细说明。

    使用command + shift + 0快捷键出来的文档,大家也可以参考。

    NSRunloop的本质

    NSRunloop是对CFRunloop的封装。

    构成元素

    NSRunloop主要作用

    NSRunloop主要有以下作用:

    • 使程序一直运行并接受用户输入
    • 决定程序在何时处理一些Event
    • 调用解耦(Message Queue)
    • 节省CPU时间(没事的时候闲着,有事的时候处理)

    依赖NSRunloop的类和框架

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

    NSRunloop消息类型

    下面我们看一下消息类型,其实就是很经典那个图。

    NSRunloop消息类型
    • Port
      监听程序的Mach ports,Mach ports是一个比较底层的东西,可以简单的理解为:内核通过port这种方式将信息发送,而mach则监听内核发来的port信息,然后将其整理,打包发给runloop。

    • Customer
      很明显,由开发人员自己发送。不仅仅是发送,过程的话相当复杂,苹果也提供了一个CFRunLoopSource来帮助处理。由于很少用到,可以简单说下核心,但是对帮助我们理解runloop却很有帮助:

      • 定义输入源(数据结构)
      • 将输入源添加到runloop,那么这样就有了接受者,即为R1。
      • 协调输入源的客户端(单独线程),专门监听消息,然后将消息打包成runloop能够处理的样式,即第一步定义的输入源。它类似Mach的功能。
      • 谁来发送消息的问题?上面的machport是由内核发送的。自定义的当然要我们自己发送了。。。首先必须是另一个线程来发送(当然如果只是测试的话可以和第三步在同一个线程),先发送消息给输入源,然后唤醒R1,因为R1一般处于休眠状态,然后R1根据输入源来做相应的处理。
    • Selector Sources
      NSObject类提供了很多方法供我们使用,这些方法是添加到runloop的,所以如果没有开启runloop的话,不会运行。

    • Timer Sources:它的事件发送是同步的,这个用的比较多。

    • Observers,观察者:首先它并不属于事件源(不会影响runloop的生命周期),它比较特殊,用于观察runloop自身的一些状态的,有以下几种:

      • 进入runloop
      • runloop即将执行定时器
      • runloop即将执行输入源(Port,Customer,Selector Sources)
      • runloop即将休眠
      • runloop被唤醒,在处理完唤醒它的事件之前
      • 退出

    NSRunloop API文档

    下面我们就看一下苹果给我们预留的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其中的一个分类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其中的一个分类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
    

    从这个API文档上我们可以看见,提供的是一个本类,两个分类(NSRunLoopConveniencesNSOrderedPerform)。下面以表格的形式给出。

    模块 内容
    获取Runloop及其模式 @property(class, readonly, strong) NSRunLoop *currentRunLoop;
    @property(readonly, copy) NSRunLoopMode currentMode;
    - (NSDate *)limitDateForMode:(NSRunLoopMode)mode;
    @property(class, readonly, strong) NSRunLoop *mainRunLoop;
    - (CFRunLoopRef)getCFRunLoop;
    定时器管理 - (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原理详解——不再有盲点

    后记

    未完,待续~~~

    相关文章

      网友评论

        本文标题:NSRunloop简单细说(一)—— 整体了解

        本文链接:https://www.haomeiwen.com/subject/lwjldxtx.html