复盘一次令人头大的上线
昨晚我负责的产品 1.6.0 正式版上线,距离正式版上线前已经上了两个灰度测试版,每次版本上线前都会遇到一些或大或小的阻断性 bug ,用我们从淘宝新来 android 老大哥的话来说:“看来 app 不论大小,上线前都是一样的难受”。
遇到的问题#
第一个问题#
昨天中午遇到的第一个情况是这样的,报表列表的置顶数据不正常,每当一个报表被置顶且成功后,再取消置顶,则会顺带把报表的收藏状态给取消了,而且 “收藏”、” 有权限 “、” 申请中 “三个接口拿到的报表数据实体置顶字段居然不一致 = =。真是头大,做了确认后给相关 RD 发了接口错误信息,给的答复是跟着下周的例行迭代一起修复,也就是说,这个版本我们要带上错误的接口上线了,问题解决,接着上课。
第二个问题#
第二个问题,刷新 “有权限” tab 时因为线上数据量大,接口有点慢,这就导致部分用户(PM)等不了(一般 3s 后如果数据未展示用户量会流失 50% ),此时切回了 “收藏” tab,最后再切回到 “申请中中” tab 时,列表数据居然是 “收藏” 的 = =,改了改,解决。
第三个问题#
第三个问题,在 “报表搜索” vc 中的 “有权限” tab 收藏报表时,侧滑 cell 后点击收藏,没有提示也不知道是收藏没收藏上,此时去 “报表超市” 查看 “收藏” tab 时,发现实际上并没有收藏,这个问题之前有遇到过,实际上是报错了的,因为收藏了重复的报表,但因为在 “报表搜索” 中 cell 没有收藏 icon ,错以为没有收藏导致重复收藏失败。最后发现其实判断报表是否收藏的字段用错了,改了一波,解决。
第四个问题#
第四个问题一直搞到今天中午,Jenkins 上打出来的 release 包推到下载平台上,1.5.0 升级上来后居然是 debug 环境 = =,刚开始觉得不应该,但是又不想解决了,到了昨晚九点时已经不想做了,就直接把 #ifdef DEBUG
下替换的 hostName 改为了线上地址,重新打包,发现问题还在。leader 怀疑是 1.5.0 上的 cache 问题,而我怀疑是 DEBUG
宏的问题,我们一直围绕在 1.6.0 本身的问题上,因为当时的情况是,拿到升级包的下载地址后,我用 safari 打开后下载是正常的,但通过 1.5.0 升级下载的包居然还是 debug 环境,一直懵逼到夜里 12 点,因为 DEBUG
这个宏不可能有错啊,差点陷入了深深的怀疑当中。
第五个问题#
一直到今天中午,无论在在 1.6.0 代码上做的任何操作,都无效,其它同事因为不是做 iOS 的,给的意见虽然也都还行,但是实际上我还是觉得 DEBUG
这个默认提供的宏没啥问题,但突然猛的发现,我应该去调 1.5.0 的代码啊,因为问题是出在 1.5.0 升级上来,而不是 1.6.0 本身,突然发现了有这么一段代码,通过 NSUserDefault
set 了取的 hostName 宏,NSUserDefault
是用户偏好设置啊 = =,升级不会被清空,能够被备份,而我在 1.6.0 中同样的方法中做了类似的 set hostName 的替换 debug 与 release 环境域名的操作,但是因为原 NSUserDefault
的 firstInitLocal
key 对应的 value 已经备份了,且已被设置为了 YES
,并没有因为 app 的升级而被清空,根本没能进入到重新 set hostName 的代码, = =,最终的做法是在 1.6.0 上注释掉这个 set hostName 的方法,重新在预编译文件中设置唯一 hostName。
第六个问题#
本以为能够高高兴兴的发了版,但却发现从 mdm(一个统一登录权限管理 app)唤起 app 时居然闪退了,重现的步骤是得先把 app 退出,然后通过 mdm 唤起 app,但是如果 app 还在后台,直接唤起没有问题,此时已经是中午 12 点多,脑子有点懵逼了(饿了),但是问题没解决这么能去吃饭呢?但是这个问题没法调试,因为 app 已经被杀掉了,看不到调试信息。想在我的手机上操作复现一下,发现 mdm 无法安装,用 leader 的手机进行调试发现该设备未在对应证书下注册,因为之前我们的企业证书下的注册设备量满了,曾经给了一段时间让大家把活跃设备重新注册,但当时 leader 的设备并没有进行注册,此时突然想到 PM 的手机注册过了,拿来一调,确实是能够重现这个问题,但却发现连上 Xcode 后居然没拿到对应的 crash log。
接着又发现就算设备不能使用 Xcode 进行调试,也能够拿到 log,还是通过 Xcode 获取,也可以通过设置 - 隐私 - 分析数据里看到所有 app 的 crash log,又把 leader 的设备拿过来连上了 Xcode 格式化了 log,变成了如下:
Thread 0 name: Dispatch queue: com.apple.main-thread
Thread 0 Crashed:
0 DiDiData 0x0000000100ff9974 0x100f54000 + 678260
1 DiDiData 0x0000000100ff84d8 0x100f54000 + 672984
2 DiDiData 0x0000000100ff3fc8 0x100f54000 + 655304
3 DiDiData 0x0000000100ff57cc 0x100f54000 + 661452
4 UIKitCore 0x00000001e9bd1380 -[UIViewController loadViewIfRequired] + 1000
5 UIKitCore 0x00000001e9bd17b0 -[UIViewController view] + 28
6 UIKitCore 0x00000001e9b2a114 -[UINavigationController _startCustomTransition:] + 1136
7 UIKitCore 0x00000001e9b3ebd4 -[UINavigationController _startDeferredTransitionIfNeeded:] + 716
8 UIKitCore 0x00000001e9b400a8 -[UINavigationController __viewWillLayoutSubviews] + 164
9 UIKitCore 0x00000001e9b22298 -[UILayoutContainerView layoutSubviews] + 224
10 UIKitCore 0x00000001ea637f44 -[UIView+ 14184260 (CALayerDelegate) layoutSublayersOfLayer:] + 1380
11 QuartzCore 0x00000001c1971a34 -[CALayer layoutSublayers] + 184
12 QuartzCore 0x00000001c19769c4 CA::Layer::layout_if_needed+ 1317316 (CA::Transaction*) + 324
13 QuartzCore 0x00000001c18d59d4 CA::Context::commit_transaction+ 657876 (CA::Transaction*) + 340
14 QuartzCore 0x00000001c19042f4 CA::Transaction::commit+ 848628 () + 608
15 QuartzCore 0x00000001c190515c CA::Transaction::observer_callback+ 852316 (__CFRunLoopObserver*, unsigned long, void*) + 92
16 CoreFoundation 0x00000001bd31db94 __CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__ + 32
17 CoreFoundation 0x00000001bd318828 __CFRunLoopDoObservers + 412
18 CoreFoundation 0x00000001bd318dc8 __CFRunLoopRun + 1264
19 CoreFoundation 0x00000001bd3185b8 CFRunLoopRunSpecific + 436
20 GraphicsServices 0x00000001bf58c584 GSEventRunModal + 100
21 UIKitCore 0x00000001ea194bc8 UIApplicationMain + 212
22 DiDiData 0x0000000100fc8778 0x100f54000 + 477048
23 libdyld.dylib 0x00000001bcdd8b94 start + 4
只需要看主线程报的 log 就好了,根据堆栈提示可以猜出是 UINavigationController
下的 UIViewController
初始化错误(如果你的 log 看不到格式化好的信息自行搜索一番如何格式化),而用到了的 UINavigationController
的地方是在 AppDelegate
中的 [self initTabBarController]
方法,刚开始我还真以为就是这个问题,然后把 tabBar
四大入口的 vc 都一行行代码注释掉查看,最后折腾了一圈发现其实跟这个问题没有关系。
又陷入了苦闷之中,在 mdm 对接群中请教了相关 RD,我的问题是:“iOS app 在前台的时候,mdm 唤起正常,app 被杀掉后,通过 mdm 唤起闪退”,他给我的回答是 “应该是处理 openURL 回调和 didFinishLaunchingWithOptions 回调事务处理逻辑问题”,后来仔细一想确实是这两个方法中才能引发这个问题,首先确保了 mdm 唤起 app 时走的 openURL
回调方法逻辑正常,已经做了 scheme 判断,那问题就出在了 didFinishLaunchingWithOptions
方法中。
又采用了最原始的方法,把这部分的代码全部注释掉,一段一段代码块打开注释并执行,最后发现原来又是上一个实习同学挖的坑,每次 app 重新加载后都注册一遍友盟的消息推送的方法(这个方法是用来初始化消息推送 push 的),1.5.0 写的时候估计这个同学没有考虑到我们的 app 会存在被其它应用唤起的情况,只考虑到了点击消息通知栏内容唤起的情况,直接从键值对中取值,但是从 mdm 唤起 app 带上的数据并不符合他写的解析代码,也没有判空,最后对空值操作,引发了 crash
怎么解决的?#
第七个问题#
最后一个问题,SSO 团队收回了一个字段,导致 app 的一个三方 H5 应用入口 ticket 验证失败,疯狂递归唤起 mdm 进行用户登录授权,允许授权后,更新的 ticket 又因为 SSO 的问题,导致 ticket 验证失败,又唤起 mdm 进行用户登录授权......,最后这个 H5 应用的团队把负责 SSO 团队的同学骂了一顿,重新上线
想骂人#
OK,解决完了以上问题,除了睡觉的八个小时还有一些零零碎碎的时间,从昨天早上十点到今天下午四点,今天一来还被 “关进” 一间会议室五个人陪着做封闭式开发,嗯,这个版本迭代周期从七月份到现在,不但长还臭。
本作品采用《CC 协议》,转载必须注明作者和本文链接
推荐文章: