如何根治《你的 App 在 iOS 13 上被卡死了吗?》

4,042 阅读7分钟

首先先上引用原文《你的 App 在 iOS 13 上被卡死了吗?》搭配阅读,以表敬意。

在本文中您将看到如下内容

  1. 如何通过数据统计后台进行简单的数据分析锁定 App 故障出现的版本

  2. 如何利用 gitlab 进行两个版本间的代码比对

  3. 逆向工具 Hopper 如何帮助我们检索关键类名、方法名

一.

从2019年12月下旬开始,陆续接到B端用户反馈,我们的B端App出现了卡死的现象,完全无法使用,其他App都好好的,对少量的问题用户造成了严重影响,甚至有用户要求退款。接到反馈后我们自然不敢大意,经过细致的排查和查询资料,最终确定用户的症状和58团队的文章《你的 App 在 iOS 13 上被卡死了吗?》中描述的完全一致,而且我们手头仅有的一台曾经复现过问题的手机中相关的崩溃日志确实有剪切板相关的内容,自此,问题原因基本确定。

58团队的分析文章令我们受益匪浅,特别是文章前半部分的原理分析和问题定位实操有很强的借鉴意义,但针对最终的解决方案和数据统计结果实在不敢苟同:

为了了解App无法启动的情况,我们在启动时添加了启动异常计数的埋点策略,当启动失败次数达到3次时就进行埋点并上报。同时为了优化用户体验,启动失败次数达到3次时则进入启动修复页面,提示用户去重启设备。通过对该策略的埋点数据分析,每天大约会有万分之二的用户会触发连续三次启动失败的问题。虽然App启动失败还有许多其他的原因,但剪贴板卡死这个问题的影响应该还是比较大的。

其一:最终的解决方案是“提示用户去重启设备”,也就是从根本上来说这并没有彻底解决用户的问题。当然,58团队肯定有很大的妥协和不得已,毕竟项目团队大了之后一个大版本的更新实在千丝万缕,想要定位问题难上加难。

其二:“每天大约会有万分之二的用户会触发连续三次启动失败”,这就意味着还有很多用户是启动失败了一次,两次的;所以实际影响的用户数要比万分之二大得多,从我们接到的反馈来看,这个数字可能会达到百分之一上下。

如果说我们的崩溃率行业标准如果是万分之五的话,百分之一其实已经是一个非常大的数字了,这个数字无论是用户还是公司,肯定都是无法接受的。而且C端产品和B端产品很不同,C端产品用户不爽了,转身就会离开;而B端用户如果遇到了使用障碍,他们会死揪住不放,然后让你退钱的。。。 虽然我们可以通过一些沟通措施,甩锅给苹果 iOS 13 系统,甩锅给不严谨的第三方 SDK,但是老板请咱们来干活儿是帮公司解决问题的,而不是甩锅给不可控的操作系统和平台。

问题分析到这里局势已经非常明显了,问题复现概率较大,一旦复现用户完全无法使用,所以必须修复!

二.

那么修复从何入手呢?App卡死并没有产生崩溃,所以我们无法通过 Crash 统计后台获取到崩溃信息; 既然问题是从12月下旬开始发生的,且最早的用户反馈日期是12月23日,第一反应肯定是看看那个时间节点附近是否有新的版本发布。我们的各条业务线都接入了神策数据分析,我们可以先分析一下神策数据中的各版本的用户量数据。

从上图我们可以看到,每次新版本发布,都会有一个新老版本用户量的自然更迭过程,老版本用户量下降,新版本用户量上升,而4.0.41版本的发布时间正好吻合了用户反馈的最早时间,至此我们可以断定,问题代码是4.0.41版本出现的。

接下来就是对比分析两个版本之间代码的异同了,我使用的方式是分别回退代码到4.0.40和4.0.41版本并创建版本分支,然后在Gitlab后台创建Merge Request进行代码比对,效果如下图:

由于之前58的文章已经分析过了,第三方 SDK 中会修改 OpenUDID 的类名前缀换成自己的前缀完成私有化,这就可以将排查范围缩小到各种第三方库的版本中了,也就是排查 Podfile 和 Podfile.lock 中的修改即可.下面是部分 Podfile.lock 修改截图:

最终排查出,此版本升级了包括极光推送,ShareSDK,融云,听云,MJRefresh 等多个 SDK,那么是否每个三方库都需要回退回去呢?当然不是,不包含 OpenUDID 代码的三方库自然可以排除嫌疑,那么又该如何确认哪些 SDK 可能包含 OpenUDID 代码呢?

接下来就需要祭出逆向大杀器 Hopper 来分析一下头文件了,免费版试用30分钟已经足够用了。将 Xcode 打好的 .ipa 包重命名为 .zip ,然后解压缩 -> Payload -> 查看包内容,找到项目同名 Unix 可执行文件拖进 Hopper 开始分析,由于不是 AppStore 的包所以不涉及到解密步骤,通过搜索 “UDID” 关键字可以查询到大量可疑方法名。

从 Hopper 搜索结果可以看出包括极光,支付宝,微信,融云等 SDK 都包含了 UDID 关键字,虽然不能完全确认其内部逻辑,但至少这一步大大缩小了排查范围。接下来的工作就很明朗了——回退所有可疑三方库到 4.0.40 App 版本中指定的 SDK 版本。

还有一个问题是,我们在4.0.41版本适配了 Xcode11,并且首次使用了 Xcode11.3 打包上传到 AppStore ,所以要不要舍弃 Xcode11.3 而采用老版本的 Xcode10.3 来打包呢,最终我们还是采用了 Xcode10.3 打包,虽然问题分析到现在 Xcode 的嫌疑已经微乎其微了,但是为了保证绝对的稳妥,必须要保证所有已知的风险因素全部回退到稳定状态,打包工具自然也是其中的一环,所以不要嫌麻烦,也不要有侥幸心理,回归 Xcode10.3 的怀抱。。。

最终回退SDK版本并使用Xcode10.3打出新包之后已经连续几天没有收到新的用户反馈了,所以基本可以断定问题已经得到了根治,如果想要进一步确定到底是哪个SDK的哪个版本引发了这个问题可以采用打日志的方式进行,一般 Crash 统计 SDK 都包含日志组件,可以满足基本需求。

截止到目前还有两个问题需要解决:

  1. 是否以后都只能使用 Xcode10.3 打包了呢?

    答: 我觉得是不必的,我们在排查过程中,用户的耐心已经快耗尽了,所以没有针对这个影响因子再做单独的测试,等以后版本稳定以后可以控制住其他变量,特别是第三方 SDK 版本,然后只改变 Xcode 版本来排除这个可能性

  2. 如何能找到实际引发问题的 SDK 呢?

    答:如果手头能有设备稳定复现这个问题是最好的,可以通过上文提到的打 Log 的方式定位问题,而且这个问题还可能是项目里的多个SDK共同作用的结果,所以想要抽丝剥茧定位到核心原因还需要更多的时间和测试,但是原理是共通的.

希望本文能对有类似困扰的朋友有所帮助。最后祝大家新春快乐,鼠年大吉~

参考:

1.《你的 App 在 iOS 13 上被卡死了吗?》

2.Hopper : www.hopperapp.com/