iOS runloop 内部构造

runloop

 runloop 死循环,运行循环, 事件循环。

我们打开手机,app一直运行不会退出,当我们操作app时,app会立即给我们相应的响应,依赖的就是runloop;

runloop 作用:

  •  运行循环,保证线程不退出,一直运行
  • 当任务来时,会唤醒线程执行任务
  • 没有任务时,线程进入休眠状态,节省系统资源

在OSX/iOS 系统中,runloop相关对象:NSRunLoop 和 CFRunLoopRef。 

CFRunloopRef源码 

首先了解几个结构体

__CFRunLoop 

  • __CFRunLoop
  • __CFRunLoopMode
  • __CFRunLoopSource
  • __CFRunLoopObserver
  • __CFRunLoopTimer
struct __CFRunLoop {
    CFRuntimeBase _base;
    pthread_mutex_t _lock;			/* locked for accessing mode list */
    __CFPort _wakeUpPort;			// used for CFRunLoopWakeUp 
    Boolean _unused;
    volatile _per_run_data *_perRunData;              // reset for runs of the run loop
    pthread_t _pthread; //runloop所在的线程 每一个runloop对应一个线程
    uint32_t _winthread;
    CFMutableSetRef _commonModes; // 存储commonMode中的mode 默认commonMode 中含有两个mode
    CFMutableSetRef _commonModeItems; //存储commonModelItem中的(source, observer, timer)
    CFRunLoopModeRef _currentMode; //runloop当前所在的mode
    CFMutableSetRef _modes; //__CFRunLoopMode 一个线程中存在多个mode
    struct _block_item *_blocks_head;
    struct _block_item *_blocks_tail;
    CFTypeRef _counterpart;
};

使用 timer需要加入到commonModel下 默认commonModel中包含kCFRunLoopDefaultMode 和 UITrackingRunLoopMode;

kCFRunLoopDefaultMode 正常状态, UITrackingRunLoopMode scrollView 滚动状态

__CFRunLoopMode 

  • 一个runloop 对应一个线程
  • 一个runloop 中对应多个mode
  • 切换mode需要退出当前mode,mode中的item时独立存在,在当前mode下只能处理当前mode对应的 commonModelItem,同一时间runloop只存在一种mode状态
  • commonMode对应多个mode,添加进commonMode的item,循环添加进commonModel中所有mode中 
struct __CFRunLoopMode {
    CFRuntimeBase _base;
    pthread_mutex_t _lock;	/* must have the run loop locked before locking this */
    CFStringRef _name;
    Boolean _stopped;
    char _padding[3];
    CFMutableSetRef _sources0; //当前mode下source0
    CFMutableSetRef _sources1; //当前mode下source1
    CFMutableArrayRef _observers; //当前mode下observer 
    CFMutableArrayRef _timers;    //当前mode下timer 
    CFMutableDictionaryRef _portToV1SourceMap;
    __CFPortSet _portSet; //基于mach_port的source1
    CFIndex _observerMask;
#if USE_DISPATCH_SOURCE_FOR_TIMERS
    dispatch_source_t _timerSource;
    dispatch_queue_t _queue;
    Boolean _timerFired; // set to true by the source when a timer has fired
    Boolean _dispatchTimerArmed;
#endif
#if USE_MK_TIMER_TOO
    mach_port_t _timerPort;
    Boolean _mkTimerArmed;
#endif
#if DEPLOYMENT_TARGET_WINDOWS
    DWORD _msgQMask;
    void (*_msgPump)(void);
#endif
    uint64_t _timerSoftDeadline; /* TSR */
    uint64_t _timerHardDeadline; /* TSR */
};

CF_INLINE void __CFRunLoopModeLock(CFRunLoopModeRef rlm) {
    pthread_mutex_lock(&(rlm->_lock));
    //CFLog(6, CFSTR("__CFRunLoopModeLock locked %p"), rlm);
}
  • 可以多个 source0/source1,observer,timer,timer 
  • 当mode为__CFRunLoop的currentMode (source0/source1,observer,timer,timer )才会执行

 __CFRunLoopSource

struct __CFRunLoopSource {
    CFRuntimeBase _base;
    uint32_t _bits;
    pthread_mutex_t _lock;
    CFIndex _order;			/* immutable */
    CFMutableBagRef _runLoops;
    union {
	CFRunLoopSourceContext version0;	/* immutable, except invalidation */
        CFRunLoopSourceContext1 version1;	/* immutable, except invalidation */
    } _context;
};

事件源,只有source0和source1

  •  Source0 只包含了一个回调(函数指针),它并不能主动触发事件。使用时,你需要先调用 CFRunLoopSourceSignal(source),将这个 Source 标记为待处理,然后手动调用 CFRunLoopWakeUp(runloop) 来唤醒 RunLoop,让其处理这个事件。
  • source1包含一个mach_port和回调指针,source1用于内核和其他线程直接的消息,source1能唤醒线程。

 __CFRunLoopObserver

struct __CFRunLoopObserver {
    CFRuntimeBase _base;
    pthread_mutex_t _lock;
    CFRunLoopRef _runLoop; //runloop 观察的runloop
    CFIndex _rlCount;
    CFOptionFlags _activities;		/* immutable */ //runloop状态
    CFIndex _order;			/* immutable */
    CFRunLoopObserverCallBack _callout;	/* immutable */ 回调指针
    CFRunLoopObserverContext _context;	/* immutable, except invalidation */
};

__CFRunLoopObserver 包含观察的runloop 和回调指针,当runloop状态变化时,回调用回调函数 回调时间点

typedef CF_OPTIONS(CFOptionFlags, CFRunLoopActivity) {
    kCFRunLoopEntry         = (1UL << 0), // 即将进入Loop
    kCFRunLoopBeforeTimers  = (1UL << 1), // 即将处理 Timer
    kCFRunLoopBeforeSources = (1UL << 2), // 即将处理 Source
    kCFRunLoopBeforeWaiting = (1UL << 5), // 即将进入休眠
    kCFRunLoopAfterWaiting  = (1UL << 6), // 刚从休眠中唤醒
    kCFRunLoopExit          = (1UL << 7), // 即将退出Loop
};

 __CFRunLoopTimer

struct __CFRunLoopTimer {
    CFRuntimeBase _base;
    uint16_t _bits;
    pthread_mutex_t _lock;
    CFRunLoopRef _runLoop; //runloop 
    CFMutableSetRef _rlModes; //加入的mode
    CFAbsoluteTime _nextFireDate;
    CFTimeInterval _interval;//间隔		/* immutable */
    CFTimeInterval _tolerance;//误差          /* mutable */
    uint64_t _fireTSR;			/* TSR units */
    CFIndex _order;			/* immutable */
    CFRunLoopTimerCallBack _callout;	/* immutable */ //回调
    CFRunLoopTimerContext _context;	/* immutable, except invalidation */
};

 CFRunLoopTimerRef 是基于时间的触发器,它和 NSTimer 是toll-free bridged 的,可以混用。其包含一个时间长度和一个回调(函数指针)。当其加入到 RunLoop 时,RunLoop会注册对应的时间点,当时间点到时,RunLoop会被唤醒以执行那个回调。

他们的关系是:

参考资料:

深入理解runloop

runloop 官方文档

猜你喜欢

转载自blog.csdn.net/wangchuanqi256/article/details/89677081