前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >UITabbarController 偶现启动crash问题分析

UITabbarController 偶现启动crash问题分析

作者头像
落影
发布2022-10-28 11:17:32
7780
发布2022-10-28 11:17:32
举报
文章被收录于专栏:落影的专栏落影的专栏

问题背景

最近新版本发布后,出现了一个偶现的crash并且迅速增加为Top1,这里对该问题做一个分析。 报错内容如下: NSException -[UITabBarController setSelectedViewController:] only a view controller in the tab bar controller's list of view controllers can be selected. crash堆栈如下:

问题分析

APM分析

首先是查看APM提供的信息,没有系统聚集、没有机型聚集,是版本新增问题都是隐私界面(也就是新用户冷启场景)。

该问题在灰度有出现过,一位同事在排查过程中,发现另外一个类似问题是在UITabBarController的 _viewControllerForTabBarItem:方法出现异常,这个问题量级并不大,场景类似但是没有特别信息帮助定位。

多维分析

由于crash出现在系统的UITabBarController类,无法调试获取更多信息,逆向排查周期太长。这里可以通过Slardar的信息,结合日志和业务场景逐步缩小排查范围。 首先通过crash场景,我们猜测是在用户新用户冷启才会遇到,这里通过回捞日志和crash的pv/uv相比可以确定; 其次通过排查新用户冷启场景的特有逻辑,关注点放在新版本相关的代码和实验改动,发现在底tab在新用户冷启场景的底tab刷新逻辑有较大可疑。 结合crash信息only a view controller in the tab bar controller's list of view controllers can be selected以及crash堆栈里有viewWillAppear时机,合理猜测一个场景:是否tab切换时,导致某个vc不在tabbar的子vc里面。比如说,没有某个tab但是又指定跳到该vc,类似self.tabbarVC setSelectedViewController:self.xxxVC;又或者,某个子vc不在self.viewConrollers里面,但是又要跳转到该vc。

通过业务代码排查,业务并无直接设置setSelectedViewController的操作;在排查过程中发现只有setSelectedIndex的操作,从堆栈上来看,如果是setSelectedIndex触发crash,堆栈上应该会有这个方法。 于是重点排查子vc不存在的情况,在查看新用户切换tab的逻辑时,发现了有一个vc复用的逻辑,旧tabbarVC的vc会被复用到新的tabbarVC,结合ViewController只能有一个parentVC的限制,从逻辑上分析是有可能出现堆栈所描述的场景。 结合这个猜测,当vc被复用到新的tabbarVC时,加了一段代码让新的tabbarVC不添加到window,从而旧的tabbar继续触发viewWillAppear,问题可以复现。

反向分析

当问题可以稳定复现后,就可以进一步分析逻辑上的缺陷。 首先是vc的复用逻辑分析: App在启动时就要初始化tabbarVC,并且在后续会刷新底tab的数量。由于我们使用了某个tabbarVC的组件,组件并不支持动态新增底tab,这里采用的是重新创建tabbarVC的方式。 而我们的vc复用逻辑就是将vc从旧的tabbarVC移到新的tabbarVC。 这里写了一个复用的模拟代码:

代码语言:javascript
复制
- (void)testAnotherTabbarVC {
    UITabBarController *anotherTabbarVC = [UITabBarController new];
    [anotherTabbarVC addChildViewController:self.tabVC.viewControllers.firstObject];
}

复用逻辑比较简单清晰,但是UIKit有一个限制:每个vc只能有一个parentVC。当我们给新tabbarVC设置子vc,其中复用vc已经有parentVC,此时因为复用到新的tabbarVC,parentVC也会从旧的tabbarVC变成新的tabbarVC。 当旧的tabbarVC触发viewWillAppear的时候,复用vc的parentVC已经变成新的tabbarVC(截图为nil是因为新的tabbarVC被释放了),但是没被复用的另外一个vc的parentVC仍然是旧tabbarVC。 此时出现了错误: only a view controller in the tab bar controller's list of view controllers can be selected

问题解决

方案1:在viewWillAppear之前,不触发reloadTab,也就是等待展示之后再把旧的tabbarVC替换为新的tabbarVC;(这也是之前采用的方案) 方案2:在设置新的tabbarVC的viewController属性时,将复用vc从旧的tabbarVC的viewController移除;(这是UIKit的默认做法,但是需要修改tabbarVC的组件) 方案3:不复用vc,只复用数据源;(需要修改复用方案)

代码地址

为了验证分析没有出错,特意写了demo,问题可以复现,github地址

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2022-10-15,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 问题背景
  • 问题分析
    • APM分析
      • 多维分析
        • 反向分析
        • 问题解决
        • 代码地址
        领券
        问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档