ios-Runloop简单介绍

来源:互联网 发布:苹果ios10性能优化 编辑:程序博客网 时间:2024/05/21 10:16

RunLoop指的是NSRunloop或者CFRunloopRef,CFRunloopRef是纯C的函数,而NSRunloop仅仅只是对CFRunloopRef的OC封装。查看CFRunLoop.h文件中的源码,我们可以发现有下面的这一段代码typedef struct __CFRunLoop * CFRunLoopRef; 这就说明CFRunLoopRef 是指向结构体 struct __CFRunLoop的指针类型。下面是CFRunLoop的结构体

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对应的那个线程    uint32_t _winthread;    CFMutableSetRef _commonModes;    CFMutableSetRef _commonModeItems;    CFRunLoopModeRef _currentMode;//当前运行的mode    CFMutableSetRef _modes;    struct _block_item *_blocks_head;    struct _block_item *_blocks_tail;    CFTypeRef _counterpart;};
再看下CFRunLoopMode的
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;    CFMutableSetRef _sources1;    CFMutableArrayRef _observers;    CFMutableArrayRef _timers;//定时器    CFMutableDictionaryRef _portToV1SourceMap;    __CFPortSet _portSet;    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 */};
从源码看Runloop总是运行在某种特定的CFRunLoopModeRef下,一个Runloop可以包含多种模式。

在我们在手机上点击应用程序,启动应用程序的时候,操作系统会创建一个线程也就是我们应用程序的主线程去调用main函数。来启动应用程序,其中主线程之所以一直存在的原因是因为开启了Runloop,Runloop可以保持主线程一直存在也就是保证应用程序不退出以及监听事件比如说触摸事件,定时器事件等等。还有就是其实一个 RunLoop 包含若干个 Mode,每个Mode又包含若干个Source/Timer/Observer。每次RunLoop启动时,我们只能指定其中的一个Mode,这个Mode就是结构体中的currentMode,如果我们需要切换Mode,只能退出Loop,再重新去指定一个Mode再进入。主要是为了分隔开不同组的Source/Timer/Observer,让其互不影响。

Runloop有五种模式如下所示,一部分资料摘抄网络

kCFRynLoopDefaultMode:App的默认模式,通常主线程是在这个Mode下运行,一般来说在默认模式下处理网络事件和timer事件
UITrackingRunLoopMode:其实也就是UI模式,也就是说我们在scrollView滚动的时候或者TextView滚动的时候便会在这个模式下,在这个模式下处理UI
kCFRunLoopCommonModes:这是一个占位Mode,不是一种真正的Mode,就是系统为了我们使用方便而设立的
UIInitializationRunLoopMode:在刚启动App时进入的第一个Mode,启动完成后不再使用
GSEventReceiveRunLoopMode:接受系统事件的内部Mode,通常用不到

其实我们把定时器加到 NSRunLoopCommonModes模式中其实就是相当于把定时器加到了两种模式中,一种是NSDefaultRunLoopMode另外一种是UITrackingRunLoopMode

我们如果在使用定时器的时候,如果我们把定时器加到了主线程的运行循环中,我们又做了耗时的操作,会阻塞主线程,所以我们如果要做耗时操作就放在子线程中执行。

这里还要介绍下的是,我们在程序中当然也可以让主线程挂掉,只需要在主线程执行的代码中实现[NSThread exit]; 只是这样的话,无论我们点什么都没有反应了,因为主线程已经挂掉了,操作系统的线程池当中已经没有这个线程的,所以肯定也就无法再去处理事件了,我们之前一直让主线程去显示一些控件,显示在界面其实是显示在操作系统上面的。也就是说只是操作系统去问app显示什么东西而已。

在一个应用程序中如果主线程挂掉了,但是子线程还一直存在的话,子线程是不会挂掉的。因为子线程是由CPU调度的。


简单的说下RunloopMode的一个架构

Source就是输入源,输入源可以分为soucre0和source1,其中source0表示的是非系统的内核事件,source1表示的是系统的内核事件。

就比如说touchesBegan方法做了些处理,一个点击事件,这个事件就可以说是一个soucre0。由自己触发管理,source1就是可以监听系统端口和其他线程相互发送消息,它能够主动唤醒RunLoop由Runloop和操作系统内核进行管理。Timers就是定时源。就是我们加入到运行循环中的定时器。

Observer就是观察者,可以监听Runloop的状态的变化

struct __CFRunLoopObserver {    CFRuntimeBase _base;    pthread_mutex_t _lock;    CFRunLoopRef _runLoop;    CFIndex _rlCount;    CFOptionFlags _activities;/* immutable */    CFIndex _order;/* immutable */    CFRunLoopObserverCallBack _callout;/* immutable */    CFRunLoopObserverContext _context;/* immutable, except invalidation */};

比如可以这么用

/**创建观察者  参数一: 指定给observer分配存储空间,这里采取默认的,参数二: 需要监听的状态类型,参数三: 是不是每次都要去监听,参数四: 优先级,参数五: 监听到状态改变之后的回调*/CFRunLoopObserverRef  observer = CFRunLoopObserverCreateWithHandler(CFAllocatorGetDefault(), kCFRunLoopAllActivities, YES, 0, ^(CFRunLoopObserverRef observer, CFRunLoopActivity activity) {            switch (activity) {                case kCFRunLoopEntry:                    NSLog(@"进入runloop");                    break;                case kCFRunLoopBeforeTimers:                    NSLog(@"即将开始处理timer");                    break;                case kCFRunLoopBeforeSources:                    NSLog(@"即将开始处理source");                    break;                case kCFRunLoopBeforeWaiting:                    NSLog(@"即将进入睡眠");                    break;                case kCFRunLoopAfterWaiting:                    NSLog(@"从睡眠中被唤醒");                    break;                case kCFRunLoopExit:                    NSLog(@"即将退出");                    break;                default:                    break;            }        });        //给主运行循环添加观察者,参数一给哪个Runloop添加观察者,第二个观察者对象,第三个在哪种模式下监听        CFRunLoopAddObserver(CFRunLoopGetMain(), observer, kCFRunLoopDefaultMode);

CFRunLoopGetCurrent()表示的是获取当前的运行循环,最后还有个释放对象,CFRelease(observer);

原创粉丝点击