【卡顿检测】深入理解

卡顿监控 红框里才是真正的判断

 

1、即将要睡眠  2、即将要运行(唤醒)

第一个 Observer 的  order 调整到 LONG_MIN

进入 kCFRunLoopAfterWaiting 状态时,第一个被调用,用于监控 Runloop 处于 运行状态

第二个 Observer 的 order  调整到 LONG_MAX

进入  kCFRunLoopBeforeWaiting 状态时,最后一个被调用,用于判断 Runloop 处于 睡眠状态

如下图所示,通过 双 Observer ,我们可以更加准确的判断Runloop 的运行状态,从而对卡顿进行更加有效的监控。

为了调整回调的执行顺序,我们需要先了解 __CFRunLoopDoObservers 函数的执行逻辑。

 
/* rl is locked, rlm is locked on entrance and exit */
static void __CFRunLoopDoObservers(CFRunLoopRef rl, CFRunLoopModeRef rlm, CFRunLoopActivity activity) __attribute__((noinline));
static void __CFRunLoopDoObservers(CFRunLoopRef rl, CFRunLoopModeRef rlm, CFRunLoopActivity activity) { /* DOES CALLOUT */
    
    cf_trace(KDEBUG_EVENT_CFRL_IS_DOING_OBSERVERS | DBG_FUNC_START, rl, rlm, activity, 0);
    
    CHECK_FOR_FORK();
    // 获取 runLoopMode 的 observer 数量,如果小于1,则直接返回
    CFIndex cnt = rlm->_observers ? CFArrayGetCount(rlm->_observers) : 0;
    if (cnt < 1) return;
 
    /* Fire the observers */
    STACK_BUFFER_DECL(CFRunLoopObserverRef, buffer, (cnt <= 1024) ? cnt : 1);
    CFRunLoopObserverRef *collectedObservers = (cnt <= 1024) ? buffer : (CFRunLoopObserverRef *)malloc(cnt * sizeof(CFRunLoopObserverRef));
    CFIndex obs_cnt = 0;
    // 1、顺序遍历 _observers,
    // 因为每个 observer 可以观察不同的 activity,所以,需要通过 & 操作符过滤需要触发的 observer
    // 并组成新的数组 collectedObservers
    for (CFIndex idx = 0; idx < cnt; idx++) {
        CFRunLoopObserverRef rlo = (CFRunLoopObserverRef)CFArrayGetValueAtIndex(rlm->_observers, idx);
        // 【避免递归】1、通过 __CFRunLoopObserverIsFiring 判断是否处于执行状态
        if (0 != (rlo->_activities & activity) && __CFIsValid(rlo) && !__CFRunLoopObserverIsFiring(rlo)) {
            collectedObservers[obs_cnt++] = (CFRunLoopObserverRef)CFRetain(rlo);
        }
    }
    __CFRunLoopModeUnlock(rlm);
    __CFRunLoopUnlock(rl);
    // 2、顺序遍历 collectedObservers
    for (CFIndex idx = 0; idx < obs_cnt; idx++) {
        CFRunLoopObserverRef rlo = collectedObservers[idx];
        __CFRunLoopObserverLock(rlo);
        if (__CFIsValid(rlo)) {
            // 【非重复 observer】1、记录是否属于非重复 observer
            Boolean doInvalidate = !__CFRunLoopObserverRepeats(rlo);
            // 【避免递归】2、回调前,通过 __CFRunLoopObserverSetFiring 记录执行的状态
            __CFRunLoopObserverSetFiring(rlo);
            __CFRunLoopObserverUnlock(rlo);
            CFRunLoopObserverCallBack callout = rlo->_callout;
            void *info = rlo->_context.info;
            cf_trace(KDEBUG_EVENT_CFRL_IS_CALLING_OBSERVER | DBG_FUNC_START, callout, rlo, activity, info);
            // 3、执行 observer 的回调
            __CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__(callout, rlo, activity, info);
            cf_trace(KDEBUG_EVENT_CFRL_IS_CALLING_OBSERVER | DBG_FUNC_END, callout, rlo, activity, info);
            // 【非重复 observer】2、非重复 observer,在回调完毕后,直接销毁
            if (doInvalidate) {
                CFRunLoopObserverInvalidate(rlo);
            }
            // 【避免递归】3、回调后,通过 __CFRunLoopObserverUnsetFiring 恢复状态
            __CFRunLoopObserverUnsetFiring(rlo);
        } else {
            __CFRunLoopObserverUnlock(rlo);
        }
        CFRelease(rlo);
    }
    __CFRunLoopLock(rl);
    __CFRunLoopModeLock(rlm);
 
    if (collectedObservers != buffer)
        free(collectedObservers);
    
    cf_trace(KDEBUG_EVENT_CFRL_IS_DOING_OBSERVERS | DBG_FUNC_END, rl, rlm, activity, 0);
}
 

__CFRunLoopDoObservers 会先遍历 _observers ,并根据各种条件组成一个新的数组 collectedObservers

新的数组生成后,会再次遍历 collectedObservers,并通过 __CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__ 回调监控函数

得到第一个重要的结论:通过控制 _observers 数组的排列顺序,能够改变调用时机。

CFRunLoopAddObserver 函数内部会根据 CFRunLoopObserverRef 的 _order 逆序遍历 CFRunLoopRef 的 _observers,并找到合适的位置进行插入

具体的源码如下所示:

// 添加 observer
void CFRunLoopAddObserver(CFRunLoopRef rl, CFRunLoopObserverRef rlo, CFStringRef modeName) {
    CHECK_FOR_FORK();
    CFRunLoopModeRef rlm;
    // 如果 runloop 处于销毁状态,直接返回
    if (__CFRunLoopIsDeallocating(rl)) return;
    // 如果主线程已经停止执行,则直接返回
    if (__CFMainThreadHasExited && rl == CFRunLoopGetMain()) {
        static dispatch_once_t onceToken;
        dispatch_once(&onceToken, ^{
            CFLog(kCFLogLevelError, CFSTR("Attempting to add observer to main runloop, but the main thread has exited. This message will only log once. Break on _CFRunLoopError_MainThreadHasExited to debug."));
        });
        _CFRunLoopError_MainThreadHasExited();
        return;
    }
    // 合规性校验 & 防止重入
    if (!__CFIsValid(rlo) || (NULL != rlo->_runLoop && rlo->_runLoop != rl)) return;
 
    __CFRunLoopLock(rl);
    // 1、如果监听 kCFRunLoopCommonModes,则遍历 _commonModes,并进行监听
    if (modeName == kCFRunLoopCommonModes) {
        CFSetRef set = rl->_commonModes ? CFSetCreateCopy(kCFAllocatorSystemDefault, rl->_commonModes) : NULL;
        if (NULL == rl->_commonModeItems) {
            rl->_commonModeItems = CFSetCreateMutable(kCFAllocatorSystemDefault, 0, &kCFTypeSetCallBacks);
        }
        CFSetAddValue(rl->_commonModeItems, rlo);
        if (NULL != set) {
            CFTypeRef context[2] = {rl, rlo};
            /* add new item to all common-modes */
            CFSetApplyFunction(set, (__CFRunLoopAddItemToCommonModes), (void *)context);
            CFRelease(set);
        }
    } else {
        rlm = __CFRunLoopFindMode(rl, modeName, true);
        if (NULL != rlm && NULL == rlm->_observers) {
            rlm->_observers = CFArrayCreateMutable(kCFAllocatorSystemDefault, 0, &kCFTypeArrayCallBacks);
        }
        if (NULL != rlm && !CFArrayContainsValue(rlm->_observers, CFRangeMake(0, CFArrayGetCount(rlm->_observers)), rlo)) {
                Boolean inserted = false;
                // 2、逆序遍历 _observers,并找到合适的位置进行插入
                for (CFIndex idx = CFArrayGetCount(rlm->_observers); idx--; ) {
                    CFRunLoopObserverRef obs = (CFRunLoopObserverRef)CFArrayGetValueAtIndex(rlm->_observers, idx);
                    if (obs->_order <= rlo->_order) {
                        CFArrayInsertValueAtIndex(rlm->_observers, idx + 1, rlo);
                        inserted = true;
                        break;
                    }
                }
                if (!inserted) {
                CFArrayInsertValueAtIndex(rlm->_observers, 0, rlo);
                }
            rlm->_observerMask |= rlo->_activities;
            __CFRunLoopObserverSchedule(rlo, rl, rlm);
        }
        if (NULL != rlm) {
            __CFRunLoopModeUnlock(rlm);
        }
    }
    __CFRunLoopUnlock(rl);
}
 

参考:

原文:https://blog.csdn.net/sinat_35969632/article/details/111351356

matrix/WCBlockMonitorMgr.mm at master · Tencent/matrix · GitHub

打不开可以试着把 github.com替换为hub.fgit.ml

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值