You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

iOS C++中如何获取应用挂起时停止计时的稳定/单调时钟

iOS C++ 实现应用挂起即停止的单调计时方案

方案1:基于进程CPU时钟的纯C++实现(无侵入)

该方案使用POSIX标准的CLOCK_PROCESS_CPUTIME_ID时钟,它统计当前进程所有线程实际占用CPU的总时间,应用被系统挂起时进程停止调度,时钟计数会自动停止,完全匹配需求,无需额外对接iOS系统逻辑。

实现代码

#include <time.h>
#include <stdint.h>

// 获取当前进程CPU时间,单位为纳秒
uint64_t get_process_running_time_ns() {
    struct timespec ts;
    clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &ts);
    return static_cast<uint64_t>(ts.tv_sec) * 1000000000ULL + static_cast<uint64_t>(ts.tv_nsec);
}

// 耗时统计示例
void measure_target_operation() {
    uint64_t start_time = get_process_running_time_ns();
    
    // 此处放置需要测算耗时的业务逻辑
    
    uint64_t end_time = get_process_running_time_ns();
    uint64_t cost_ns = end_time - start_time;
    // 按需转换单位:毫秒 = cost_ns / 1e6,秒 = cost_ns / 1e9
}

适用场景说明

  • 优点:纯C++跨POSIX平台实现,天然单调稳定,不受系统时间修改、应用挂起影响,无需额外适配
  • 缺点:仅统计进程实际占用CPU的时间,操作中的IO等待、线程休眠时间不会被计入。如果需要统计操作从触发到完成的全链路真实耗时(仅排除挂起时间),请使用方案2。

方案2:基于生命周期监听的挂起时长扣除实现

该方案以系统单调时钟为基础,通过监听应用前后台切换事件统计总挂起时长,最终从总耗时中扣除挂起时长,实现仅统计应用活跃状态的时间。

实现逻辑

  1. 选择CLOCK_MONOTONIC作为基础计时时钟(不受系统时间修改影响,仅整机重启才会重置)
  2. 监听UIApplicationDidEnterBackground通知,记录应用进入后台的时间点
  3. 监听UIApplicationWillEnterForeground通知,计算本次挂起的时长并累加到全局总挂起时长
  4. 最终耗时 = 操作结束时钟时间 - 操作开始时钟时间 - 操作执行期间的总挂起时长

核心代码(Objective-C实现,保存为.mm文件即可在C工程中调用)

#include <time.h>
#include <stdint.h>
#import <UIKit/UIKit.h>

static uint64_t g_total_suspend_ns = 0;
static uint64_t g_enter_background_time_ns = 0;

// 初始化生命周期监听,在应用启动时调用一次即可
void init_suspend_aware_timer() {
    [[NSNotificationCenter defaultCenter] addObserverForName:UIApplicationDidEnterBackgroundNotification object:nil queue:nil usingBlock:^(NSNotification * _Nonnull note) {
        struct timespec ts;
        clock_gettime(CLOCK_MONOTONIC, &ts);
        g_enter_background_time_ns = static_cast<uint64_t>(ts.tv_sec) * 1000000000ULL + static_cast<uint64_t>(ts.tv_nsec);
    }];
    
    [[NSNotificationCenter defaultCenter] addObserverForName:UIApplicationWillEnterForegroundNotification object:nil queue:nil usingBlock:^(NSNotification * _Nonnull note) {
        if (g_enter_background_time_ns == 0) return;
        struct timespec ts;
        clock_gettime(CLOCK_MONOTONIC, &ts);
        uint64_t current = static_cast<uint64_t>(ts.tv_sec) * 1000000000ULL + static_cast<uint64_t>(ts.tv_nsec);
        g_total_suspend_ns += (current - g_enter_background_time_ns);
        g_enter_background_time_ns = 0;
    }];
}

// 获取扣除挂起时间的单调时钟值,单位纳秒
uint64_t get_suspend_aware_time_ns() {
    struct timespec ts;
    clock_gettime(CLOCK_MONOTONIC, &ts);
    uint64_t current = static_cast<uint64_t>(ts.tv_sec) * 1000000000ULL + static_cast<uint64_t>(ts.tv_nsec);
    return current - g_total_suspend_ns;
}

// 耗时统计示例
void measure_target_operation() {
    uint64_t start_time = get_suspend_aware_time_ns();
    
    // 此处放置需要测算耗时的业务逻辑
    
    uint64_t end_time = get_suspend_aware_time_ns();
    uint64_t cost_ns = end_time - start_time;
}

适用场景说明

  • 优点:统计的是应用活跃状态下的真实流逝时间,包含IO等待、线程休眠等非CPU占用时间,符合绝大多数业务耗时统计的需求
  • 注意:需要在应用启动时调用一次init_suspend_aware_timer初始化监听逻辑,若应用开启了后台任务权限,后台任务运行期间的时间会被正常计入,仅应用被系统彻底挂起后才会停止计时。

内容的提问来源于stack exchange,提问作者randomThought

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.03 13:36:00