iOS 编程:跟踪内存、CPU、电池电量

图片来自Pixabay

跟踪可用和已用的内存方法

💡在出现低内存警告时对应用进行埋点,包括内存的使用及统计信息,并在应用重新运行时将这些信息上报给服务器。使用这些信息来识别出应用发生内存溢出的常见场景和边缘情况。

示例代码

// HPMemoryAnalyzer.h
#import <Foundation/Foundation.h>

/**
 跟踪可用和已用的内存
 */
@interface HPMemoryAnalyzer : NSObject

vm_size_t getUsedMemory();
vm_size_t getFreeMemory();

@end

// HPMemoryAnalyzer.m
#import "HPMemoryAnalyzer.h"
#import <mach/mach.h>

vm_size_t getUsedMemory() {
    task_basic_info_data_t info;
    mach_msg_type_number_t size = sizeof(info);
    kern_return_t kerr = task_info(mach_task_self(), TASK_BASIC_INFO, (task_info_t)&info, &size);
    
    if (kerr == KERN_SUCCESS) {
        return info.resident_size;
    }else {
        return 0;
    }
}

vm_size_t getFreeMemory() {
    mach_port_t host = mach_host_self();
    mach_msg_type_number_t size = sizeof(vm_statistics_data_t) / sizeof(integer_t);
    vm_size_t pagesize;
    vm_statistics_data_t vmstat;
    
    host_page_size(host, &pagesize);
    host_statistics(host, HOST_VM_INFO, (host_info_t)&vmstat, &size);
    
    return vmstat.free_count * pagesize;
}

@implementation HPMemoryAnalyzer

@end

另外,YYKit 框架中也集成了关于获取应用内存使用量、CPU使用量的相关方法:

// UIApplication+YYAdd.h

/// Current thread real memory used in byte. (-1 when error occurs)
@property (nonatomic, readonly) int64_t memoryUsage;

/// Current thread CPU usage, 1.0 means 100%. (-1 when error occurs)
@property (nonatomic, readonly) float cpuUsage;


// UIApplication+YYAdd.m
- (int64_t)memoryUsage {
    struct task_basic_info info;
    mach_msg_type_number_t size = sizeof(info);
    kern_return_t kern = task_info(mach_task_self(), TASK_BASIC_INFO, (task_info_t)&info, &size);
    if (kern != KERN_SUCCESS) return -1;
    return info.resident_size;
}

- (float)cpuUsage {
    kern_return_t kr;
    task_info_data_t tinfo;
    mach_msg_type_number_t task_info_count;
    
    task_info_count = TASK_INFO_MAX;
    kr = task_info(mach_task_self(), TASK_BASIC_INFO, (task_info_t)tinfo, &task_info_count);
    if (kr != KERN_SUCCESS) {
        return -1;
    }
    
    thread_array_t thread_list;
    mach_msg_type_number_t thread_count;
    
    thread_info_data_t thinfo;
    mach_msg_type_number_t thread_info_count;
    
    thread_basic_info_t basic_info_th;
    
    kr = task_threads(mach_task_self(), &thread_list, &thread_count);
    if (kr != KERN_SUCCESS) {
        return -1;
    }
    
    long tot_sec = 0;
    long tot_usec = 0;
    float tot_cpu = 0;
    int j;
    
    for (j = 0; j < thread_count; j++) {
        thread_info_count = THREAD_INFO_MAX;
        kr = thread_info(thread_list[j], THREAD_BASIC_INFO,
                         (thread_info_t)thinfo, &thread_info_count);
        if (kr != KERN_SUCCESS) {
            return -1;
        }
        
        basic_info_th = (thread_basic_info_t)thinfo;
        
        if (!(basic_info_th->flags & TH_FLAGS_IDLE)) {
            tot_sec = tot_sec + basic_info_th->user_time.seconds + basic_info_th->system_time.seconds;
            tot_usec = tot_usec + basic_info_th->system_time.microseconds + basic_info_th->system_time.microseconds;
            tot_cpu = tot_cpu + basic_info_th->cpu_usage / (float)TH_USAGE_SCALE;
        }
    }
    
    kr = vm_deallocate(mach_task_self(), (vm_offset_t)thread_list, thread_count * sizeof(thread_t));
    assert(kr == KERN_SUCCESS);
    
    return tot_cpu;
}

除了 UIApplication+YYAdd 中这两个方法,你会发现 UIDevice+YYAdd 中还有更丰富的方法,包括获取设备信息、磁盘空间、内存信息、CPU信息学。

电池电量与代码感知

一个智能的应用会考虑到电池的电量和自身的状态,从而决定是否要真正执行资源密集消耗型的操作。另外一个有价值的点是对充电的判断,确定设备是否处于充电状态。
例如:每次更新iOS系统时,系统会判断当前设备电量是否低于50%,如果低于这个值,系统是不会执行更新系统的任务的,它会提示用户先充电之后再更新系统。

示例代码

传入执行特定操作需要的最低电量级别。该级别是浮点数,范围在 0~100 之间(100表示电池完全充满)

-(BOOL)shouldProceedWithMinLevel:(int)minLevel
{
    UIDevice *device = [UIDevice currentDevice];
    device.batteryMonitoringEnabled = YES;

    UIDeviceBatteryState state = device.batteryState;
    if(state == UIDeviceBatteryStateCharging || state == UIDeviceBatteryStateFull) {
        return YES;
    }

    int batteryLevel = (int) (device.batteryLevel * 100);
    if(batteryLevel >= minLevel) {
        return YES;
    }
    return NO;
}

谨慎使用电量的最佳实践

  • 最小化硬件使用。换句话说,尽可能晚地与硬件打交道,并且一旦完成任务立即结束使用。
  • 在进行密集型任务前,检查电池电量和充电状态。
  • 在电量低时,提示用户是否确定要执行任务,并在用户同意后再执行。
  • 或提供设置的选项,允许用户定义电量的阈值,以便在执行密集型操作前提示用户。

参考

  • Gaurav Vaish. 高性能iOS应用开发 [M]. 北京:人民邮电出版社,2017. 66-67

推荐阅读更多精彩内容

  • 国家电网公司企业标准(Q/GDW)- 面向对象的用电信息数据交换协议 - 报批稿:20170802 前言: 排版 ...
    庭说阅读 7,519评论 6 13
  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 158,448评论 24 688
  • 虽然不确定看到的是不是流星,还是在早上六点多,但月亮+流星应该还是标配吧,天有异象该怎么办?睡一觉吧!
    heim_dn阅读 66评论 0 0
  • 还掌控不了自己的情绪,就谈不上成熟
    Astro7阅读 201评论 0 0
  • 最近读了《当你的才华还撑不起你的梦想时》这本书,觉得有些话说得很好,在这里想和大家分享一下一些印象深刻的话: 土豪...
    安七月阅读 71评论 0 0