使用os_signpost进行iOS代码耗时分析


一、os_signpost 是什么

os_signpost 是 Apple 提供的一套用于标记代码性能的工具,能够帮助开发者精准测量和分析代码执行的耗时及行为。它是 os_log 系统的一部分,可以与 Instruments 的 Points of Interest 和 Time Profiler 等模板结合使用。

  • os_log_t:日志句柄,用于标识一个子系统和类别。
  • os_signpost_id_t:标记 ID,用于区分不同的事件或时间区间。
  • os_signpost 类型:
  • os_signpost_event_emit:标记一个瞬时事件。
  • os_signpost_interval_begin / os_signpost_interval_end:标记时间区间的开始和结束。

二、使用方法

1. 必要的头文件

使用 os_signpost 需要导入头文件:

#import <os/log.h>
#import <os/signpost.h>

2. 创建 os_log_t 日志句柄

os_log_t 用于标识日志的子系统和分类,通常初始化一次并在整个类中复用:

os_log_t log = os_log_create("com.yourapp.subsystem", "Category");

第一个参数:subsystem - 一般是应用的标识符(如 com.yourapp)。
第二个参数:category - 分类名称(如 Performance、PointsOfInterest)。

3. os_signpost 的核心用法

标记时间区间(Interval)

用于测量一段代码的执行时间。

os_log_t log = os_log_create("com.yourapp.performance", "ImageDownload");
os_signpost_id_t signpostID = os_signpost_id_generate(log);

// 开始标记
os_signpost_interval_begin(log, signpostID, "Image Download");

// 模拟耗时操作
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 1 * NSEC_PER_SEC), dispatch_get_main_queue(), ^{
    // 结束标记
    os_signpost_interval_end(log, signpostID, "Image Download");
});

标记瞬时事件(Event)

用于记录一个单一的事件,例如方法调用、状态变化。

os_log_t log = os_log_create("com.yourapp.performance", "PointsOfInterest");
os_signpost_id_t signpostID = os_signpost_id_generate(log);

os_signpost_event_emit(log, signpostID, "Single Event Occurred");

为时间区间添加元数据

可以为区间或事件标记附加格式化的元数据,以便在 Instruments 中更好地分析。

os_log_t log = os_log_create("com.yourapp.performance", "PointsOfInterest");
os_signpost_id_t signpostID = os_signpost_id_generate(log);

int imageIndex = 5;

// 带元数据开始标记
os_signpost_interval_begin(log, signpostID, "Image Download Index: %d", imageIndex);

// 模拟下载完成
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 1 * NSEC_PER_SEC), dispatch_get_main_queue(), ^{
    os_signpost_interval_end(log, signpostID, "Image Download Finished Index: %d", imageIndex);
});

三、优点

  • 轻量级:对性能影响极小,可以安全地用于生产环境。
  • 精准追踪:支持瞬时事件和时间区间的跟踪。
  • 可视化分析:与 Instruments 集成,提供直观的时间线和数据。

四、使用的全流程

首先准备一段代码,并设置好要分析代码耗时的区间:

#import "ViewController.h"
#import <os/log.h>
#import <os/signpost.h>

@interface ViewController () <UICollectionViewDataSource, UICollectionViewDelegateFlowLayout>

@property (nonatomic, strong) UICollectionView *collectionView;
@property (nonatomic, strong) NSMutableArray<UIImage *> *images;
@property NSMutableArray<NSString *> *imageUrlString;
@property (nonatomic, strong) os_log_t log;
@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    self.log = os_log_create("com.wml.getPic", "PointsOfInterest");
    
    os_signpost_id_t initViewID = os_signpost_id_generate(self.log);
    os_signpost_interval_begin(self.log, initViewID, "ViewDidLoad - begin");
    
    // 初始化图片数组
    self.images = [NSMutableArray array];
    
    // 创建并配置 UICollectionView
    UICollectionViewFlowLayout *layout = [[UICollectionViewFlowLayout alloc] init];
    layout.itemSize = CGSizeMake(100, 100);
    layout.minimumInteritemSpacing = 10;
    layout.minimumLineSpacing = 10;
    layout.sectionInset = UIEdgeInsetsMake(10, 10, 10, 10); // 设置边距
    
    self.collectionView = [[UICollectionView alloc] initWithFrame:self.view.bounds collectionViewLayout:layout];
    self.collectionView.dataSource = self;
    self.collectionView.delegate = self;
    [self.collectionView registerClass:[UICollectionViewCell class] forCellWithReuseIdentifier:@"cell"];
    self.collectionView.backgroundColor = [UIColor whiteColor];
    
    [self.view addSubview:self.collectionView];
    os_signpost_interval_end(self.log, initViewID, "ViewDidLoad - begin");
    
    // 下载图片
    [self downloadImages];
    NSLog(@"下载图片ing...");
}

- (void)downloadImages {
    os_signpost_id_t getUrlId = os_signpost_id_generate(self.log);
    os_signpost_interval_begin(self.log, getUrlId, "Init Image Url - start");
    
    // 假设我们有一个包含 100 个图片 URL 的数组
    NSMutableArray<NSURL *> *imageURLs = [NSMutableArray array];
    [self initURLs];
    for (int i = 0; i < self.imageUrlString.count; i++) {
        NSString *urlString = self.imageUrlString[i];
        [imageURLs addObject:[NSURL URLWithString:urlString]];
    }
    os_signpost_interval_end(self.log, getUrlId, "Init Image Url - start");
    
    for (NSURL *url in imageURLs) {
        NSURLSessionDataTask *task = [[NSURLSession sharedSession] dataTaskWithURL:url completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
            
            os_signpost_id_t downloadImageID = os_signpost_id_generate(self.log);
            os_signpost_interval_begin(self.log, downloadImageID, "Download Image - start");
            
            if (data && !error) {
                UIImage *image = [UIImage imageWithData:data];
                if (image) {
                    @synchronized (self.images) {
                        [self.images addObject:image];
                    }
                    dispatch_async(dispatch_get_main_queue(), ^{
                        os_signpost_interval_end(self.log, downloadImageID, "Download Image - start");
                        [self.collectionView reloadData];
                    });
                }
            }
        }];
        
        [task resume];
    }
    
}

#pragma mark - UICollectionViewDataSource

- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section {
    return self.images.count;
}

- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
    UICollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:@"cell" forIndexPath:indexPath];
    
    // 移除旧的UIImageView
    for (UIView *subview in cell.contentView.subviews) {
        [subview removeFromSuperview];
    }
    
    UIImageView *imageView = [[UIImageView alloc] initWithFrame:cell.contentView.bounds];
    imageView.image = self.images[indexPath.item];
    imageView.contentMode = UIViewContentModeScaleAspectFit;
    
    [cell.contentView addSubview:imageView];
    
    return cell;
}

- (void)initURLs {
    self.imageUrlString = @[
    @"https://s2.loli.net/2024/07/03/xOo3V2btpDKIMJE.jpg",
    @"https://s2.loli.net/2024/07/03/Cj2P4QA7OKdxDwu.jpg",
    @"https://s2.loli.net/2024/07/03/ODqIC73HGhKoamF.jpg",
    @"https://s2.loli.net/2024/07/03/MR7NweS54E6uWln.jpg",
    @"https://s2.loli.net/2024/07/03/n7TrzS21FAuta5X.jpg",
    @"https://s2.loli.net/2024/07/03/uI4RdqWAcjPQ5HJ.jpg",
    @"https://s2.loli.net/2024/07/03/Wr6wzbGoc1q7Zk5.jpg",
    @"https://s2.loli.net/2024/07/03/kYAfKo2zc3hGvn6.jpg",
    @"https://s2.loli.net/2024/07/03/j5qmWdMZ9wgP8Rx.jpg",];
}

点击Product中的Profile:

在这里插入图片描述

然后选择:

在这里插入图片描述

点击运行:

在这里插入图片描述

最后我们可以看到耗时的统计数据:

在这里插入图片描述

五、注意事项

根据提示,我们需要将 os_log_create 的第二个参数设置为 PointsOfInterest 才能捕获到耗时!

os_log_t log = os_log_create("com.yourapp.subsystem", "PointsOfInterest");

在这里插入图片描述

这两个方法的第三个参数要一致,才能捕获到区间代码的耗时!

os_signpost_interval_begin(log, interval_id, name, ...)
os_signpost_animation_interval_begin(log, interval_id, name, ...)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值