--------------------------------------------------------------------------
4月9号,苹果开始向iOS用户推送最新系统版本iOS8.3的升级。手机QQ团队第一时间进行系统升级的兼容性验证,发现在图片选择器界面切换标清图和原图时必现闪退现象。同时,在微博、论坛和support平台等渠道均有收到大量用户反馈此问题。
于是,我们迅速在厂内的崩溃统计分析平台(小编注: 即Bugly平台
)查找相应崩溃问题的堆栈信息进行分析。
初步分析发现崩溃问题定位在UIView addSubview
的调用,并有明确的错误信息:
<span style="font-size:12px;">Terminating app due to uncaught exception 'UIApplicationInvalidInterfaceOrientation', reason: 'Supported orientations has no common orientation with the application, and [QQMarkActionSheetController_FixPos shouldAutorotate] is returning YES'</span>
我们进行了真机联调测试,验证崩溃问题必现场景同崩溃分析平台记录的一致。
再分析崩溃信息的详情,可以明确崩溃问题是由于QQMarkActionSheetController_FixPos
的方向和application
的方向不一致导致的。
于是,按照如下方法修复此问题:
1. 查找源码定位QQMarkActionSheetController_FixPos
继承自UIAlertController
2. 重写shouldAutorotate
方法,返回值设为NO
重新编译调式验证,崩溃问题果然解决。
但正所谓“福无双至,祸不单行”,我们继续深入一些隐蔽场景测试,又发现两个必现崩溃的场景:
· 编辑图片后选择取消
· 关闭Wi-Fi后发送短视频
分析对比后,发现这两个场景有一个共同的业务逻辑,即是弹出UIAlertView
进行消息提示,且崩溃的位置和错误的信息和前面提到的崩溃问题很相似:
<span style="font-size:12px;">Supported orientations has no common orientation with the application, and [_UIAlertShimPresentingViewController shouldAutorotate] is returning YES</span>
也是由于_UIAlertShimPresentingViewController
的方向和application
的方向不一致导致应用崩溃。
注意,此时,我们就不能跟前面提到的崩溃问题采用同样的方法进行修复了!
因为,_UIAlertShimPresentingViewController
是系统内部的类,我们没法重写其shouldAutorotate
方法。
所以,我们开始怀疑是否在iOS8.3系统中,是不是所有调用UIAlertView
的地方都会发生崩溃?
但在选择了几个调用UIAlertView
的界面进行验证后,发现并没有崩溃发生。这种情况让我们很是困惑,在一番探索后,我们把焦点转移到项目中二次封装的SimpleAlertView
上,尝试把出现崩溃场景的UIAlertView
换成SimpleAlertView
,联调测试验证后发现崩溃问题没有再出现。
于是,我们得到一个解决此类崩溃问题的方法:
将工程中所有调用UIAlertView
执行UI提示的逻辑全部替换为调用SimpleAlertView
执行,当然,还需根据不同的场景修改适配UI样式和交互表现。
但古人有曰,三思而后行。
我们在工程中搜索UIAlertView
的调用,发现竟有500+的调用,分别分布在300+的文件中,如果替换的话,其潜在风险和工作量都要仔细考量,而且还需针对不同场景修改UI样式和交互表现,不可取!
所以,我们否决了此解决方法,继续分析问题并探索其他的解决方法。
在前面的验证过程中,我们发现并非所有出现UIAlertView
的界面都发生了崩溃,而是只有三个场景出现此类问题,而且都和图片选择器有关,于是把焦点又转移到图片选择器相关的逻辑,并进行了一系列的检查和验证:
1. Review代码,确认图片选择器里面的shouldAutorotate
方法返回值确实为NO
2. 测试在3G环境下拍摄短视频发送,弹出流量提示框,无崩溃发生
3. 从图片选择器界面发送短视频,弹出流量提示框时,发生崩溃
由此,我们断定问题在图片选择器的相关处理中。
于是又搜索了其他场景使用UIAlertView
但没有崩溃的代码,对比发现二者的supportedInterfaceOrientations
方法有一些差异:
· 图片选择器的视图控制器里面supportedInterfaceOrientations
方法返回值为UIInterfaceOrientationPortrait
(NSUInteger) supportedInterfaceOrientations { return UIInterfaceOrientationPortrait; }
· 其他场景的supportedInterfaceOrientations
方法返回值为UIInterfaceOrientationMaskPortrait
(NSUInteger) supportedInterfaceOrientations { return UIInterfaceOrientationMaskPortrait; }
此处必有蹊跷!
我们火速进行修复尝试:
将图片选择器界面的视图控制器的supportedInterfaceOrientations
方法返回值改为UIInterfaceOrientationMaskPortrait
编译联调验证,果然没有发生崩溃。
我们断定在iOS8.3系统出现的UIAlertView
发生崩溃的根本原因可能在于此,于是又将QQMarkActionSheetController_FixPos
中重写的shouldAutorotate
方法删除,测试验证发现没有发生崩溃,继续验证其他曾发生崩溃的场景,崩溃问题没有发生。
终于,我们得出此崩溃问题的根因。
如果在视图控制器中重写supportedInterfaceOrientations
方法,并将返回值设为UIInterfaceOrientationPortrait
的话,那么在此视图控制器或子视图中弹出UIAlertView
时,就会发生崩溃。
查阅了开发文档了解supportedInterfaceOrientations
方法的使用,发现其返回值实际是UIInterfaceOrientationMask
类型,而项目中却返回了UIInterfaceOrientation
类型,二者虽然长得很相似,但用处完全不一样:
· UIInterfaceOrientationMask
类型用来表示UIViewController
能支持的方向
· UIInterfaceOrientation
类型用来表示application
当前的方向
如果在iOS8.3系统上两者混用,系统新增的检查判断会发现此问题,并抛出异常,崩溃就这样产生了。
· 在开发过程中对于系统常量的引用及其含义要加强关注,尤其是遇到此类“兄弟”模样的常量时,避免因理解不清或笔误而造成问题。
· 在系统版本更新时,要及时关注系统API和常量定义的变化,对功能代码进行兼容性调整。
不总结哪来经验,不分享经验何用?
在此小编号召大家多总结,互分享,踊跃给我们投稿,把自己踩过并爬出来的坑树个指示牌警醒后人,让猿们的开发生活更加美好!
投稿方式:将文章和个人介绍邮件到 [email protected],字数不限
本文系腾讯Bugly特邀文章,转载请注明作者和出处“腾讯Bugly(http://bugly.qq.com)”