IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 移动开发 -> iOS 性能监控方案设计(一) -> 正文阅读

[移动开发]iOS 性能监控方案设计(一)

??最近在搞app的性能监控。主要从启动耗时,首屏耗时,操作耗时的几个指标进行监控,后续会增加其他维度的指标

启动耗时

??启动耗时主要分为冷启动,热启动。 其中冷启动又分为首次启动,非首次启动。
冷启动:从main函数开始,到第一个用户自定义的页面出现为止(备注:这个过程中要区分一下是否是首次启动)
热启动:从app即将进入前台到,app进入前台的这个过程。涉及到的函数。具体下:

- (void)applicationWillEnterForeground:(UIApplication *)application
{
   // 此处调用热启动开始的方法
}
- (void)applicationDidBecomeActive:(UIApplication *)application
{
   // 此处调用热启动结束的统计方法
}

首屏耗时

?? 首屏耗时是指从一个页面初始化到到页面获取数据后第一次渲染完成结束。
初始化的时机:hook UIViewController 的init方法,获取调用的时机
示例代码如下:

- (instancetype)performance_init
{
    NSString *className = NSStringFromClass([self class]);
    //过滤掉系统类
    if ([className hasPrefix:@"UI"]) {
        return [self performance_init];
    }
    // 过滤掉黑名单
    if ([JKPerformanceManager isInBlackList:className]) {
        return [self performance_init];
    }
    JKPagePerformanceModel *firstScreen_pagePerformanceModel = [JKPagePerformanceModel new];
    firstScreen_pagePerformanceModel.element_type = JKPageTypeFirstScreen;
    firstScreen_pagePerformanceModel.start_time = [[NSDate date] timeIntervalSince1970]  * 1000;
    firstScreen_pagePerformanceModel.page = className;
    self.firstScreen_pagePerformanceModel = firstScreen_pagePerformanceModel;
    return [self performance_init];
}

渲染完成的时机:目前只考虑有列表的视图。UITableView,UICollectionView reloadData结束后 visibleCell的数量大于某个数值。认为渲染完成。
关于如何reloaddata后获取获取visibleCell的数量可以参考《iOS开发获取tableView,collectionView reloaddata 执行结束布局生效时机》
坑点:如果是嵌套的UITableView,UICollectionView 存在reloaddata的时候,对应的视图的window不存在,这个时候采取延迟满足的策略,为这些视图打上标记。等到这些视图调用didMoveToWindow 的时候,判断视图的window是否存在,如果存在,并且该视图是被标记过的,那么这个时机可以认为是首屏渲染完成的时机。示例代码如下:

- (void)performance_didMoveToWindow
{
    [self performance_didMoveToWindow];
    //视图离开页面的时候window为nil,此时过滤掉
    if (!self.window) {
        return;
    }
    id delegate = self.delegate;
    //过滤掉异常
    if (!delegate) {
        return;
    }
    NSString *delegate_ClassName = NSStringFromClass([delegate class]);
    // 过滤掉系统子类
    if (![NSStringFromClass([self class]) isEqualToString:@"UITableView"]) {
        return;
    }
    // 过滤掉系统视图
    if ([delegate_ClassName hasPrefix:@"UI"]) {
        return;
    }
    // 忽略掉黑名单
    if ([JKPerformanceManager isInBlackList:delegate_ClassName]) {
        return;
    }
    
    [self setNeedsLayout];
    [self layoutIfNeeded];
    
    if ([[JKPerformanceManager helper] respondsToSelector:@selector(track_tableViewDidMoveToWindow:)]) {
        [[JKPerformanceManager helper] track_tableViewDidMoveToWindow:self];
    }
    [self performance_firtScreenTrack];
}

操作耗时

?? 操作耗时是指:用户的一次操作,比如点击 从开始执行,到执行结束的耗时。我这次处理的操作耗时主要有,按钮的点击,手势触发的时间。列表点击触发的事件。
UIControl的处理如下:

- (void)performance_sendAction:(SEL)action to:(id)target forEvent:(UIEvent *)event
{
    JKOperationPerformanceModel *operatePerformanceModel = [VVOperationPerformanceModel new];
    operatePerformanceModel.start_time = [[NSDate date] timeIntervalSince1970]  * 1000;
    if ([[JKPerformanceManager helper] respondsToSelector:@selector(track_control:sendAction:to:forEvent:)]) {
        [[JKPerformanceManager helper] track_control:self sendAction:action to:target forEvent:event];
    }
// 正常执行事件
    [self performance_sendAction:action to:target forEvent:event];
    // 忽略掉黑名单,避免干扰
    NSString *target_ClassName = NSStringFromClass([target class]);
    if ([JKPerformanceManager isInBlackList:target_ClassName]) {
        return;
    }
    
    // 性能采集打点
    operatePerformanceModel.end_time = [[NSDate date] timeIntervalSince1970]  * 1000;
    operatePerformanceModel.element_type = JKOperateTypeClick;
    operatePerformanceModel.widget = [NSString stringWithFormat:@"%@+%@",target_ClassName,NSStringFromSelector(action)];
    if (!self.track_containerVC) {
        UIViewController *track_containerVC = [JKPerformanceManager topContainerViewControllerOfResponder:self];
        self.track_containerVC = track_containerVC;
    }
    operatePerformanceModel.page = NSStringFromClass(self.track_containerVC.class)?:NSStringFromClass([UIViewController class]);
    [JKPerformanceManager trackPerformance:operatePerformanceModel vc:self.track_containerVC];
    
}

UIGestureRecognizer的处理如下:

- (void)handleOfTarget:(id)target selector:(SEL)selector
{
   
    // 过滤掉已经hook的
    NSMutableArray *hookedArray = (NSMutableArray *)objc_getAssociatedObject(target, track_gesture_target_has_hookedKey);
    if ([hookedArray containsObject:selectorStr]) {
        return;
    }
    NSError *error1 = nil;
    [(NSObject *)target aspect_hookSelector:selector withOptions:AspectPositionInstead usingBlock:^(id<AspectInfo> data){
        if ([[JKPerformanceManager helper] respondsToSelector:@selector(track_gesture:target:selector:)]) {
            [[JKPerformanceManager helper] track_gesture:self target:target selector:selector];
        }
        
        JKOperationPerformanceModel *operatePerformanceModel = [JKOperationPerformanceModel new];
        operatePerformanceModel.start_time = [[NSDate date] timeIntervalSince1970]  * 1000;
        
        NSInvocation *invocation = [data originalInvocation];
        [invocation invoke];
        
        if ([self isKindOfClass:[UITapGestureRecognizer class]]
             || [self isKindOfClass:[UILongPressGestureRecognizer class]]) {
            operatePerformanceModel.end_time = [[NSDate date] timeIntervalSince1970]  * 1000;
            if ([self isKindOfClass:[UITapGestureRecognizer class]]) {
                operatePerformanceModel.element_type = JKOperateTypeClick;
            } else if ([self isKindOfClass:[UILongPressGestureRecognizer class]]) {
                operatePerformanceModel.element_type = JKOperateTypeLongPress;
            }
            operatePerformanceModel.widget = [NSString stringWithFormat:@"%@+%@",target_ClassName,selectorStr];
            if (!self.view.track_containerVC) {
                UIViewController *track_containerVC = [JKPerformanceManager topContainerViewControllerOfResponder:self.view];
                self.view.track_containerVC = track_containerVC;
            }
            operatePerformanceModel.page = NSStringFromClass(self.view.track_containerVC.class)?:NSStringFromClass([UIViewController class]);
            [JKPerformanceManager trackPerformance:operatePerformanceModel vc:self.self.view.track_containerVC];
        }
        
    } error:&error1];
    
    if (error1) {
#if DEBUG
        NSLog(@"UIGestureRecognizer+JKTrack error1:%@",error1);
        NSAssert(NO, @"UIGestureRecognizer+JKTrack error1");
#endif
    } else {
        NSMutableArray *array = (NSMutableArray *)objc_getAssociatedObject(target, track_gesture_target_has_hookedKey);
        if (!array) {
            array = [NSMutableArray new];
        }
        [array addObject:selectorStr];
        objc_setAssociatedObject(target, track_gesture_target_has_hookedKey, array, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
    }
}

UITableView 的处理如下:

- (void)performance_reloadData
{
    [self performance_reloadData];
    //屏蔽掉系统子类
    if (![NSStringFromClass([self class]) isEqualToString:@"UITableView"]) {
        return;
    }
    NSString *delegate_ClassName = NSStringFromClass([self.delegate class]);
    // 过滤掉系统视图
    if ([delegate_ClassName hasPrefix:@"UI"]) {
        return;
    }
    // 忽略掉黑名单
    if ([JKPerformanceManager isInBlackList:delegate_ClassName]) {
        return;
    }
    if (!self.window) {
        if ([[JKPerformanceManager helper] respondsToSelector:@selector(track_markTableViewNOSuperView:)]) {
            [[JKPerformanceManager helper] track_markTableViewNOSuperView:self];
        }
        return;
    }
    
    [self setNeedsLayout];
    [self layoutIfNeeded];
    
    if ([[JKPerformanceManager helper] respondsToSelector:@selector(track_reloadDataOfTableView:)]) {
        [[JKPerformanceManager helper] track_reloadDataOfTableView:self];
    }
    
    [self performance_firtScreenTrack];
}

文中贴出来的代码,主要是方便大家了解一下思路。
完整代码下载地址:《JKPerformanceManager》

《iOS开发获取tableView,collectionView reloaddata 执行结束布局生效时机》
https://blog.csdn.net/hanhailong18/article/details/105243778?spm=1001.2014.3001.5501
《UIView /UIViewController的生命周期》
https://juejin.cn/post/6844904089348734983
《VC耗时时监控的参考资料》
https://blog.csdn.net/killer1989/article/details/108099921
《iOS App冷启动治理:来自美团外卖的实践》
https://segmentfault.com/a/1190000017298001

更多干货文章,欢迎扫描二维码关注功能
在这里插入图片描述

  移动开发 最新文章
Vue3装载axios和element-ui
android adb cmd
【xcode】Xcode常用快捷键与技巧
Android开发中的线程池使用
Java 和 Android 的 Base64
Android 测试文字编码格式
微信小程序支付
安卓权限记录
知乎之自动养号
【Android Jetpack】DataStore
上一篇文章      下一篇文章      查看所有文章
加:2021-08-23 16:48:38  更:2021-08-23 16:50:02 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/23 9:38:24-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码