runloop总结

作者: 大墙66370 | 来源:发表于2020-02-08 16:31 被阅读0次
    static CFMutableDictionaryRef __CFRunLoops = NULL;
    
    CFRunLoopRef CFRunLoopGetCurrent(void) {
        CHECK_FOR_FORK();
        CFRunLoopRef rl = (CFRunLoopRef)_CFGetTSD(__CFTSDKeyRunLoop);
        if (rl) return rl;
        return _CFRunLoopGet0(pthread_self());
    }
    
    CF_EXPORT CFRunLoopRef _CFRunLoopGet0(pthread_t t) {
        if (pthread_equal(t, kNilPthreadT)) {
        t = pthread_main_thread_np();
        }
        __CFLock(&loopsLock);
        if (!__CFRunLoops) {
            __CFUnlock(&loopsLock);
        //如果__CFRunLoops存放loop的字典为NULL会初始化一个字典并且根据主线程创建mainLoop.
        CFMutableDictionaryRef dict = CFDictionaryCreateMutable(kCFAllocatorSystemDefault, 0, NULL, &kCFTypeDictionaryValueCallBacks);
        CFRunLoopRef mainLoop = __CFRunLoopCreate(pthread_main_thread_np());
        CFDictionarySetValue(dict, pthreadPointer(pthread_main_thread_np()), mainLoop);
        if (!OSAtomicCompareAndSwapPtrBarrier(NULL, dict, (void * volatile *)&__CFRunLoops)) { 
            //把dict里面元素赋值给__CFRunLoops
            CFRelease(dict);
        }
        CFRelease(mainLoop);
            __CFLock(&loopsLock);
        }
        CFRunLoopRef loop = (CFRunLoopRef)CFDictionaryGetValue(__CFRunLoops, pthreadPointer(t)); //根绝线程从字典中取出loop
        __CFUnlock(&loopsLock);
        if (!loop) { 
        //如果从字典中取出的loop为NULL就创建一个newLoop
        CFRunLoopRef newLoop = __CFRunLoopCreate(t);
            __CFLock(&loopsLock);
        loop = (CFRunLoopRef)CFDictionaryGetValue(__CFRunLoops, pthreadPointer(t));
        if (!loop) {
            //不知道为啥再从字典中根据线程取一遍loop上面不是已经取过了没有取到吗? 
            //这次肯定也是取不到的啊,取不到就把newLoop存到字典中
            CFDictionarySetValue(__CFRunLoops, pthreadPointer(t), newLoop);
            loop = newLoop;
        }
            // don't release run loops inside the loopsLock, because CFRunLoopDeallocate may end up taking it
            __CFUnlock(&loopsLock);
        CFRelease(newLoop);
        }
        if (pthread_equal(t, pthread_self())) {
            _CFSetTSD(__CFTSDKeyRunLoop, (void *)loop, NULL);
            if (0 == _CFGetTSD(__CFTSDKeyRunLoopCntr)) {
                _CFSetTSD(__CFTSDKeyRunLoopCntr, (void *)(PTHREAD_DESTRUCTOR_ITERATIONS-1), (void (*)(void *))__CFFinalizeRunLoop);
            }
        }
        return loop;
    }
    
    static CFRunLoopRef __CFRunLoopCreate(pthread_t t) {
        CFRunLoopRef loop = NULL;
        CFRunLoopModeRef rlm;
        uint32_t size = sizeof(struct __CFRunLoop) - sizeof(CFRuntimeBase);
        loop = (CFRunLoopRef)_CFRuntimeCreateInstance(kCFAllocatorSystemDefault, CFRunLoopGetTypeID(), size, NULL);
        if (NULL == loop) {
        return NULL;
        }
        (void)__CFRunLoopPushPerRunData(loop);
        __CFRunLoopLockInit(&loop->_lock);
        loop->_wakeUpPort = __CFPortAllocate();
        if (CFPORT_NULL == loop->_wakeUpPort) HALT;
        __CFRunLoopSetIgnoreWakeUps(loop);
        loop->_commonModes = CFSetCreateMutable(kCFAllocatorSystemDefault, 0, &kCFTypeSetCallBacks);
        CFSetAddValue(loop->_commonModes, kCFRunLoopDefaultMode);
        loop->_commonModeItems = NULL;
        loop->_currentMode = NULL;
        loop->_modes = CFSetCreateMutable(kCFAllocatorSystemDefault, 0, &kCFTypeSetCallBacks);
        loop->_blocks_head = NULL;
        loop->_blocks_tail = NULL;
        loop->_counterpart = NULL;
        loop->_pthread = t;
    #if DEPLOYMENT_TARGET_WINDOWS
        loop->_winthread = GetCurrentThreadId();
    #else
        loop->_winthread = 0;
    #endif
        rlm = __CFRunLoopFindMode(loop, kCFRunLoopDefaultMode, true);
        if (NULL != rlm) __CFRunLoopModeUnlock(rlm);
        return loop;
    }
    

    __CFRunLoops是一个字典已线程作为key CFRunLoopRef作为value

    从以上代码分析可以看到runloop和线程的关系是一一对应的,但是子线程中的runloop默认是不存在的的 主动在子线程中调用[NSRunLoop currentRunLoop],子线程的runloop会自动创建,主线程的runloop会在__CFRunLoopCreate(pthread_t t)这个函数第一次被调用的时候创建

    runloop结构

    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;
        uint32_t _winthread;
        CFMutableSetRef _commonModes;
        CFMutableSetRef _commonModeItems;
        CFRunLoopModeRef _currentMode;
        CFMutableSetRef _modes;
        struct _block_item *_blocks_head;
        struct _block_item *_blocks_tail;
        CFAbsoluteTime _runTime;
        CFAbsoluteTime _sleepTime;
        CFTypeRef _counterpart;
    };
    

    runloop主要结构

    struct __CFRunLoop {
        CFMutableSetRef _commonModes;     // Set
        CFMutableSetRef _commonModeItems; // Set<Source/Observer/Timer>
        CFRunLoopModeRef _currentMode;    // Current Runloop Mode
        CFMutableSetRef _modes;           // Set里面存了若干个CFRunLoopModeRef
        ...
    };
    

    typedef struct __CFRunLoopMode *CFRunLoopModeRef;

    struct __CFRunLoopMode {
        CFStringRef _name;            // Mode Name, 例如 @"kCFRunLoopDefaultMode"
        CFMutableSetRef _sources0;    // Set CFRunLoopSourceRef
        CFMutableSetRef _sources1;    // Set CFRunLoopSourceRef
        CFMutableArrayRef _observers; // Array CFRunLoopObserverRef
        CFMutableArrayRef _timers;    // Array 存CFRunLoopTimerRef
        ...
    };
    

    CFRunLoopModeRef代表RunLoop的运行模式

    一个RunLoop包含若干个Mode,每个Mode又包含若干个Source0/Source1/Timer/Observer

    image.png

    RunLoop启动时只能选择其中一个Mode,作为currentMode

    如果需要切换Mode,只能退出当前Loop,再重新选择一个Mode进入

    不同组的Source0/Source1/Timer/Observer能分隔开来,互不影响

    如果Mode里没有任何Source0/Source1/Timer/Observer,RunLoop会立马退出

    CFRunLoopSourceRef 是事件产生的地方。Source有两个版本:Source0 和 Source1。
    • Source0 只包含了一个回调(函数指针),它并不能主动触发事件。使用时,你需要先调用 CFRunLoopSourceSignal(source),将这个 Source 标记为待处理,然后手动调用 CFRunLoopWakeUp(runloop) 来唤醒 RunLoop,让其处理这个事件。
    • Source1 包含了一个 mach_port 和一个回调(函数指针),被用于通过内核和其他线程相互发送消息。这种 Source 能主动唤醒 RunLoop 的线程,其原理在下面会讲到。

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

    CFRunLoopObserverRef是观察者,每个 Observer 都包含了一个回调(函数指针),当 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
        kCFRunLoopAllActivities = 0x0FFFFFFFU
    };
    

    __CFRunLoop有个概念叫 “CommonModes”:一个 Mode 可以将自己标记为”Common”属性(通过将其 ModeName 添加到 RunLoop 的 “commonModes” 中)。每当 RunLoop 的内容发生变化时,RunLoop 都会自动将 _commonModeItems 里的 Source/Observer/Timer 同步到具有 “Common” 标记的所有Mode里。

    应用场景举例:主线程的 RunLoop 里有两个预置的 Mode:kCFRunLoopDefaultMode 和 UITrackingRunLoopMode。这两个 Mode 都已经被标记为”Common”属性。DefaultMode 是 App 平时所处的状态,TrackingRunLoopMode 是追踪 ScrollView 滑动时的状态。当你创建一个 Timer 并加到 DefaultMode 时,Timer 会得到重复回调,但此时滑动一个TableView时,RunLoop 会将 mode 切换为 TrackingRunLoopMode,这时 Timer 就不会被回调,并且也不会影响到滑动操作。

    有时你需要一个 Timer,在两个 Mode 中都能得到回调,一种办法就是将这个 Timer 分别加入这两个 Mode。还有一种方式,就是将 Timer 加入到顶层的 RunLoop 的 “commonModeItems” 中。”commonModeItems” 被 RunLoop 自动更新到所有具有”Common”属性的 Mode 里去。

    runloop源码的运行流程分析

    SInt32 CFRunLoopRunSpecific(CFRunLoopRef rl, CFStringRef modeName, CFTimeInterval seconds, Boolean returnAfterSourceHandled) {     /* DOES CALLOUT */
    
        // 通知Observers进入loop
        if (currentMode->_observerMask & kCFRunLoopEntry ) __CFRunLoopDoObservers(rl, currentMode, kCFRunLoopEntry);
        // 具体要做的事情
        result = __CFRunLoopRun(rl, currentMode, seconds, returnAfterSourceHandled, previousMode);
        // 通知Observers退出loop
        if (currentMode->_observerMask & kCFRunLoopExit ) __CFRunLoopDoObservers(rl, currentMode, kCFRunLoopExit);
        return result;
    }
    
    
    
    static int32_t __CFRunLoopRun(CFRunLoopRef rl, CFRunLoopModeRef rlm, CFTimeInterval seconds, Boolean stopAfterHandle, CFRunLoopModeRef previousMode) {
        
        int32_t retVal = 0;
        do {
            
          
            __CFRunLoopUnsetIgnoreWakeUps(rl);
    
            //将要处理timer
            if (rlm->_observerMask & kCFRunLoopBeforeTimers) __CFRunLoopDoObservers(rl, rlm, kCFRunLoopBeforeTimers);
            //将要处理Sources
            if (rlm->_observerMask & kCFRunLoopBeforeSources) __CFRunLoopDoObservers(rl, rlm, kCFRunLoopBeforeSources);
            //处理block
        __CFRunLoopDoBlocks(rl, rlm);
            //处理Sources0
            Boolean sourceHandledThisLoop = __CFRunLoopDoSources0(rl, rlm, stopAfterHandle);
            if (sourceHandledThisLoop) {
                //根绝处理Sources0的结果 来处理block
                __CFRunLoopDoBlocks(rl, rlm);
        }
    
            Boolean poll = sourceHandledThisLoop || (0ULL == timeout_context->termTSR);
    
            if (MACH_PORT_NULL != dispatchPort && !didDispatchPortLastTime) {
                msg = (mach_msg_header_t *)msg_buffer;
                if (__CFRunLoopServiceMachPort(dispatchPort, &msg, sizeof(msg_buffer), &livePort, 0, &voucherState, NULL)) {
                    //如果有Sources1 跳转到handle_msg
                    goto handle_msg;
                }
            }
    
            didDispatchPortLastTime = false;
            
            //通知Observers即将休眠
        if (!poll && (rlm->_observerMask & kCFRunLoopBeforeWaiting)) __CFRunLoopDoObservers(rl, rlm, kCFRunLoopBeforeWaiting);
        __CFRunLoopSetSleeping(rl);
        // do not do any user callouts after this point (after notifying of sleeping)
    
            // Must push the local-to-this-activation ports in on every loop
            // iteration, as this mode could be run re-entrantly and we don't
            // want these ports to get serviced.
    
            __CFPortSetInsert(dispatchPort, waitSet);
            
        __CFRunLoopModeUnlock(rlm);
        __CFRunLoopUnlock(rl);
    
            CFAbsoluteTime sleepStart = poll ? 0.0 : CFAbsoluteTimeGetCurrent();
    
            do {
                if (kCFUseCollectableAllocator) {
                    // objc_clear_stack(0);
                    // <rdar://problem/16393959>
                    memset(msg_buffer, 0, sizeof(msg_buffer));
                }
                msg = (mach_msg_header_t *)msg_buffer;
                
                //等待别的消息来唤醒线程
                __CFRunLoopServiceMachPort(waitSet, &msg, sizeof(msg_buffer), &livePort, poll ? 0 : TIMEOUT_INFINITY, &voucherState, &voucherCopy);
                
                if (modeQueuePort != MACH_PORT_NULL && livePort == modeQueuePort) {
                    // Drain the internal queue. If one of the callout blocks sets the timerFired flag, break out and service the timer.
                    while (_dispatch_runloop_root_queue_perform_4CF(rlm->_queue));
                    if (rlm->_timerFired) {
                        // Leave livePort as the queue port, and service timers below
                        rlm->_timerFired = false;
                        break;
                    } else {
                        if (msg && msg != (mach_msg_header_t *)msg_buffer) free(msg);
                    }
                } else {
                    // Go ahead and leave the inner loop.
                    break;
                }
            } while (1);
    
            
            __CFRunLoopLock(rl);
            __CFRunLoopModeLock(rlm);
    
            rl->_sleepTime += (poll ? 0.0 : (CFAbsoluteTimeGetCurrent() - sleepStart));
    
            // Must remove the local-to-this-activation ports in on every loop
            // iteration, as this mode could be run re-entrantly and we don't
            // want these ports to get serviced. Also, we don't want them left
            // in there if this function returns.
    
            __CFPortSetRemove(dispatchPort, waitSet);
            
            __CFRunLoopSetIgnoreWakeUps(rl);
    
            // user callouts now OK again 结束睡觉
        __CFRunLoopUnsetSleeping(rl);
         
            //通知DoObservers结束休眠
        if (!poll && (rlm->_observerMask & kCFRunLoopAfterWaiting)) __CFRunLoopDoObservers(rl, rlm, kCFRunLoopAfterWaiting);
    
            //这里可能是是 有Sources1直接过来的
        handle_msg:;
            __CFRunLoopSetIgnoreWakeUps(rl);
    
          if (modeQueuePort != MACH_PORT_NULL && livePort == modeQueuePort) {
                //被timer执行
                __CFArmNextTimerInMode(rlm, rl);
            }
            else if (rlm->_timerPort != MACH_PORT_NULL && livePort == rlm->_timerPort) {
                //被timer执行
                __CFArmNextTimerInMode(rlm, rl);
            }
            else if (livePort == dispatchPort) {
                //被GCD唤醒
                __CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__(msg);
            } else {
                CFRUNLOOP_WAKEUP_FOR_SOURCE();
                //处理Source1
                 __CFRunLoopDoSource1(rl, rlm, rls, msg, msg->msgh_size, &reply)
            }
                
        
        //处理Blocks
        __CFRunLoopDoBlocks(rl, rlm);
            
    
        if (sourceHandledThisLoop && stopAfterHandle) {
            retVal = kCFRunLoopRunHandledSource;
            } else if (timeout_context->termTSR < mach_absolute_time()) {
                retVal = kCFRunLoopRunTimedOut;
        } else if (__CFRunLoopIsStopped(rl)) {
                __CFRunLoopUnsetStopped(rl);
            retVal = kCFRunLoopRunStopped;
        } else if (rlm->_stopped) {
            rlm->_stopped = false;
            retVal = kCFRunLoopRunStopped;
        } else if (__CFRunLoopModeIsEmpty(rl, rlm, previousMode)) {
            retVal = kCFRunLoopRunFinished;
        }
            
            voucher_mach_msg_revert(voucherState);
            os_release(voucherCopy);
    
    
        } while (0 == retVal);
    
        if (timeout_timer) {
            dispatch_source_cancel(timeout_timer);
            dispatch_release(timeout_timer);
        } else {
            free(timeout_context);
        }
    
        return retVal;
    }
    

    1通知Observers进入loop
    2通知Observers即将处理Timers
    3通知Observers即将处理Sources
    4处理Blocks
    5处理Sources0(根据返回结果 可能再次处理Blocks)
    6如果存在Sources1,直接到第8部
    7通知Observers开始休眠(等待消息唤醒)
    8通知Observers结束休眠(被某个消息唤醒)
    ---1处理Timer
    ---2处理GCD Async To Main Queue
    ---1处理Sources1
    9处理Blocks
    10根据前面的执行结果决定如何操作
    ---回到第二步
    ---退出Loop
    11通知Observers退出Loop

    image.png

    Source0 触摸事件处理 performSelector:onThread:

    Source1 基于Port的线程间通信 系统事件捕捉

    Timers NSTimer performSelector:withObject:afterDelay:

    Observers 用于监听RunLoop的状态 UI刷 (BeforeWaiting)
    Autorelease pool(BeforeWaiting)

    runloop的使用场景

    1线程保活

    #import "LCPermenantThread.h"
    #import "LCThread.h"
    
    @interface LCPermenantThread ()
    
    @property (nonatomic, strong) LCThread *thread;
    @property (nonatomic, assign, getter=isStopped) BOOL stopped;
    @end
    
    @implementation LCPermenantThread
    
    - (instancetype)init{
        if (self = [super init]) {
            self.stopped = NO;
            __weak typeof(self) weakSelf = self;
            self.thread = [[LCThread alloc]initWithBlock:^{
                [[NSRunLoop currentRunLoop] addPort:[NSPort new] forMode:NSDefaultRunLoopMode];
                while (weakSelf && !weakSelf.isStopped) {
                    NSLog(@"while+++");
                    //这里面不要用strongSelf 因为 runloop会卡在这里用strongSelf就持有了self 必须手动stop runloop
                    [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];
                    NSLog(@"while---");
                }
                NSLog(@"thread end");
            }];
        }
        return self;
    }
    
    - (void)run{
        if (!self.thread) {
            return;
        }
        [self.thread start];
    }
    
    - (void)executeTask:(LCPermenantThreadTask)task{
        if (!self.thread || !task) {
            return;
        }
        [self performSelector:@selector(__executeTask:) onThread:self.thread withObject:task waitUntilDone:NO];
    }
    
    - (void)stop{
        if (!self.thread) {
            return;
        }
        [self performSelector:@selector(__stop) onThread:self.thread withObject:nil waitUntilDone:YES];
    }
    
    - (void)__executeTask:(LCPermenantThreadTask)task{
        task();
    }
    
    - (void)__stop{
        self.stopped = YES;
        CFRunLoopStop(CFRunLoopGetCurrent());
        self.thread = nil;
    }
    
    - (void)dealloc{
        NSLog(@"%s",__func__);
        [self stop];
    }
    
    @end
    
    

    2NSTimer注意事项

    NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:1 block:^(NSTimer * _Nonnull timer) {
            
        } repeats:YES];
    

    以scheduledTimer开的头创建的的timer默认被加到了当前的runloop的default mode (schedules it on the current run loop in the default mode.)

    NSTimer *timer  = [[NSTimer alloc] initWithFireDate:nil interval:1 repeats:YES block:^(NSTimer * _Nonnull timer) {
                        
                    }];
    
    NSTimer *timer  = [NSTimer timerWithTimeInterval:1 block:^(NSTimer * _Nonnull timer) {
                        
                    } repeats:YES];
    
    [[NSRunLoop currentRunLoop] addTimer:_timer forMode:NSRunLoopCommonModes]
    

    在timer fire之前必须把timer加入到runloop中 常用到的mode NSDefaultRunLoopMode UITrackingRunLoopMode 需要的注意的是NSRunLoopCommonModes并不是一种真正的mode 我们吧timer以这样"模式"加到runloop中(不记得Runloop的结构往上面找找)其实是加在了_commonModeItems里面了. 然后会查看_commonModes里的标记的mode(里面存的是"UITrackingRunLoopMode",和"kCFRunLoopDefaultMode"两个字符串),然后把timer从_commonModeItems复制到标记的Mode中的_timers数组中, 所以 UITrackingRunLoopMode模式下的mode和kCFRunLoopDefaultMode模式下都可以执行timer了

    3卡顿检测
    下面是检查卡顿的代码利用 监听runloop中Observer发出不同的LoopActivity. 利用信号量 超时过滤出 kCFRunLoopBeforeSources 和 kCFRunLoopAfterWaiting. 因为大部分的操作就是在Sources0和Sources1中处理的 上面这两个状态就是处理Sources0和Sources1之前的状态.

    #import "LXDAppFluecyMonitor.h"
    #import "LXDBacktraceLogger.h"
    
    
    #define LXD_DEPRECATED_POLLUTE_MAIN_QUEUE
    
    
    @interface LXDAppFluecyMonitor ()
    
    @property (nonatomic, assign) int timeOut;
    @property (nonatomic, assign) BOOL isMonitoring;
    
    @property (nonatomic, assign) CFRunLoopObserverRef observer;
    @property (nonatomic, assign) CFRunLoopActivity currentActivity;
    
    @property (nonatomic, strong) dispatch_semaphore_t semphore;
    @property (nonatomic, strong) dispatch_semaphore_t eventSemphore;
    
    @end
    
    
    #define LXD_SEMPHORE_SUCCESS 0
    static NSTimeInterval lxd_restore_interval = 5;
    static NSTimeInterval lxd_time_out_interval = 1;
    static int64_t lxd_wait_interval = 200 * NSEC_PER_MSEC;
    
    
    /*!
     *  @brief  监听runloop状态为before waiting状态下是否卡顿
     */
    static inline dispatch_queue_t lxd_event_monitor_queue() {
        static dispatch_queue_t lxd_event_monitor_queue;
        static dispatch_once_t once;
        dispatch_once(&once, ^{
            lxd_event_monitor_queue = dispatch_queue_create("com.sindrilin.lxd_event_monitor_queue", NULL);
        });
        return lxd_event_monitor_queue;
    }
    
    /*!
     *  @brief  监听runloop状态在after waiting和before sources之间
     */
    static inline dispatch_queue_t lxd_fluecy_monitor_queue() {
        static dispatch_queue_t lxd_fluecy_monitor_queue;
        static dispatch_once_t once;
        dispatch_once(&once, ^{
            lxd_fluecy_monitor_queue = dispatch_queue_create("com.sindrilin.lxd_monitor_queue", NULL);
        });
        return lxd_fluecy_monitor_queue;
    }
    
    #define LOG_RUNLOOP_ACTIVITY 0
    static void lxdRunLoopObserverCallback(CFRunLoopObserverRef observer, CFRunLoopActivity activity, void * info) {
        SHAREDMONITOR.currentActivity = activity;
        dispatch_semaphore_signal(SHAREDMONITOR.semphore);
    #if LOG_RUNLOOP_ACTIVITY
        switch (activity) {
            case kCFRunLoopEntry:
                NSLog(@"runloop entry");
                break;
                
            case kCFRunLoopExit:
                NSLog(@"runloop exit");
                break;
                
            case kCFRunLoopAfterWaiting:
                NSLog(@"runloop after waiting");
                break;
                
            case kCFRunLoopBeforeTimers:
                NSLog(@"runloop before timers");
                break;
                
            case kCFRunLoopBeforeSources:
                NSLog(@"runloop before sources");
                break;
                
            case kCFRunLoopBeforeWaiting:
                NSLog(@"runloop before waiting");
                break;
                
            default:
                break;
        }
    #endif
    };
    
    @implementation LXDAppFluecyMonitor
    
    #pragma mark - Singleton override
    + (instancetype)sharedMonitor {
        static LXDAppFluecyMonitor * sharedMonitor;
        static dispatch_once_t once;
        dispatch_once(&once, ^{
            sharedMonitor = [[super allocWithZone: NSDefaultMallocZone()] init];
            [sharedMonitor commonInit];
        });
        return sharedMonitor;
    }
    
    + (instancetype)allocWithZone: (struct _NSZone *)zone {
        return [self sharedMonitor];
    }
    
    - (void)dealloc {
        [self stopMonitoring];
    }
    
    - (void)commonInit {
        self.semphore = dispatch_semaphore_create(0);
        self.eventSemphore = dispatch_semaphore_create(0);
    }
    
    
    #pragma mark - Public
    - (void)startMonitoring {
        if (_isMonitoring) { return; }
        _isMonitoring = YES;
        CFRunLoopObserverContext context = {
            0,
            (__bridge void *)self,
            NULL,
            NULL
        };
        _observer = CFRunLoopObserverCreate(kCFAllocatorDefault, kCFRunLoopAllActivities, YES, 0, &lxdRunLoopObserverCallback, &context);
        CFRunLoopAddObserver(CFRunLoopGetMain(), _observer, kCFRunLoopCommonModes);
        
        dispatch_async(lxd_event_monitor_queue(), ^{
            while (SHAREDMONITOR.isMonitoring) {
                if (SHAREDMONITOR.currentActivity == kCFRunLoopBeforeWaiting) {
                    __block BOOL timeOut = YES;
                    NSLog(@"0");
                    dispatch_async(dispatch_get_main_queue(), ^{
                        timeOut = NO;
                        dispatch_semaphore_signal(SHAREDMONITOR.eventSemphore);
                        NSLog(@"1");
                    });
                    NSLog(@"2");
                    [NSThread sleepForTimeInterval: lxd_time_out_interval];
                    NSLog(@"3");
                    if (timeOut) {
                        NSLog(@"4");
                        [LXDBacktraceLogger lxd_logMain];
                    }
                    NSLog(@"5");
                    dispatch_wait(SHAREDMONITOR.eventSemphore, DISPATCH_TIME_FOREVER);
                    NSLog(@"6");
                }
            }
        });
        
        dispatch_async(lxd_fluecy_monitor_queue(), ^{
            //while一直在循环换速度很快
            //上面observer的回调函数会在runloop状态切换的时候发信号量
            //信号量的发布频率 如果小于lxd_wait_interval 说明在这个状态超时了
            //超时dispatch_semaphore_wait的返回值不为0
            //下面过滤了当前runloop状态是kCFRunLoopBeforeSources 和 kCFRunLoopAfterWaiting两种状态超时超时了五次lxd_wait_interval(说明代码有卡顿)
            while (SHAREDMONITOR.isMonitoring) {
                long waitTime = dispatch_semaphore_wait(self.semphore, dispatch_time(DISPATCH_TIME_NOW, lxd_wait_interval)); //返回值不为0的时候   表示信号量超时
                if (waitTime != LXD_SEMPHORE_SUCCESS) {
                    if (!SHAREDMONITOR.observer) {
                        SHAREDMONITOR.timeOut = 0;
                        [SHAREDMONITOR stopMonitoring];
                        continue;
                    }
                    if (SHAREDMONITOR.currentActivity == kCFRunLoopBeforeSources || SHAREDMONITOR.currentActivity == kCFRunLoopAfterWaiting) {
                        if (++SHAREDMONITOR.timeOut < 5) {
                            continue; //提前结束本次 while循环
                        }
                        [LXDBacktraceLogger lxd_logMain];
                        [NSThread sleepForTimeInterval: lxd_restore_interval];
                    }
                }
                
                SHAREDMONITOR.timeOut = 0;
            }
        });
    }
    
    - (void)stopMonitoring {
        if (!_isMonitoring) { return; }
        _isMonitoring = NO;
        
        CFRunLoopRemoveObserver(CFRunLoopGetMain(), _observer, kCFRunLoopCommonModes);
        CFRelease(_observer);
        _observer = nil;
    }
    
    
    @end
    
    

    4性能优化
    待更新

    5点击事件也和runloop有关
    ios进程通信 一个进程向Mach port发消息 另一个进程监听Mach port
    我们知道runloop没有事情做的时候就会休眠,那我们一个点击事假是怎么唤醒runloop的呢? 中间都发生了什么事情了呢?

    //runloop的睡眠是和个函数卡主了
    __CFRunLoopServiceMachPort(waitSet, &msg, sizeof(msg_buffer), &livePort, poll ? 0 : TIMEOUT_INFINITY, &voucherState, &voucherCopy)
    

    iOS中有很多进程通信的方式Mach Ports,Distributed Notifications,Distributed Objects,XPC等等
    这个函数等待接收mach_port消息

    当我们触发了事件(触摸/锁屏/摇晃等)后
    由IOKit.framework生成一个 IOHIDEvent事件
    而IOKit是苹果的硬件驱动框架
    由它进行底层接口的抽象封装与系统进行交互传递硬件感应的事件
    它专门处理用户交互设备,由IOHIDServices和IOHIDDisplays两部分组成
    其中IOHIDServices是专门处理用户交互的,它会将事件封装成IOHIDEvents对象,详细请看这里

    然后这些事件又由SpringBoard接收,它只接收收按键(锁屏/静音等),触摸,加速,接近传感器等几种 Event
    接着用mach port转发给需要的App进程

    随后苹果注册的那个Source1就会触发回调,并调用 _UIApplicationHandleEventQueue()进行应用内部的分发
    _UIApplicationHandleEventQueue()把IOHIDEvent处理包装成UIEvent进行处理分发.我们平时的UIGesture/处理屏幕旋转/发送给 UIWindow/UIButton 点击、touchesBegin/Move/End/Cancel这些事件,都是在这个回调中完成.
    接下来就进入到寻找合适的view这个过程了
    UIApplication->UIWindow->......->合适的hitView
    找到合适的view就改响应事件了
    合适的hitView-(没有响应事件)->superView-(没有响应事件)->...(所有superView都没有响应事件)...->UIWindow-(NO)-> UIApplication(默认没有响应)->丢弃这个UIEvent
    点击事件总结借鉴(chaoxi) 哟_Json

    触摸事件过程
    触摸事件过程图片来自 JM_Seven

    相关文章

      网友评论

        本文标题:runloop总结

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