“熊猫”Bug是指一种极难出现、难以摸索原因的bug。
今天产品在众测平台上发现了一个“熊猫”bug,这个bug的表现很奇怪,弹出的UIAlertController的按钮没有文字,但title和message有文字。这个bug我们无法重现,在外网只有两个用户的手机上出现,出现之后就是必现的。
![](https://img.haomeiwen.com/i680728/fd712d21e7451269.png)
![](https://img.haomeiwen.com/i680728/ae03f7e2db08ff63.png)
这个bug刚拿到手有点茫然,不明白为什么使用系统的弹窗会有这种问题,一度怀疑是苹果的Bug。
建立猜想
由于这个bug只有外网的两个用户能重现,所以初步的方案是先补一些log, 增加一些实验的代码,再打个包让用户去试,看看能否查得原因。
由这个bug的变现,初步猜想这个bug可能成因:
-
没有在主线程弹出alertController(看用户的log排除了) -
按钮的字是白色的(这个自己实验就排除了) - 弹出时有其他动画冲突,导致没有渲染,需要手动调用
setNeedsDisplay
- 按钮layout时出了问题
-
系统的bug(bug只在Qzone出现) - 需要手动设一下tintColor
根据以上的猜想,总结出了3个解决方案,打包给用户实验:
- 弹出前手动设一下tintColor
- 弹出前手动调用
setNeedsDisplay
- 什么也不做,排除环境影响
同时在弹出时,把alertController的UI层级打印出来,判断是不是layout除了问题。
补log,打包
增加三个按钮,让用户点击后呼出弹窗,来判断上面的3种解决方案的效果:
//点击按钮时
- (IBAction)tapBugfixButton:(id)sender{
self.bugfixType = [(UIButton*)sender tag];
}
//呼出alertController
- (void)showAlert{
//alert测试包
if (self.bugfixType == 1001) {
//之前看log alertController.view.tintColor是有值的,所以再设置一遍试试
alertController.view.tintColor = alertController.view.tintColor;
QZLVLOG_INFO(@"************bugfix56118199 alertView set tintColor ************");
}else if (self.bugfixType == 1002){
[alertController.view setNeedsDisplay];
QZLVLOG_INFO(@"************bugfix56118199 alertView setNeedsDisplay ************");
}else if (self.bugfixType == 1003){
QZLVLOG_INFO(@"************bugfix56118199 alertView do nothing ************");
}
self.alertCtrl = alertController;
[self performSelector:@selector(needDescribeUI) withObject:nil afterDelay:0];
}
//打印UI层级
- (void)needDescribeUI{
NSString *description = [self.alertCtrl.view performSelector:@selector(recursiveDescription)];
QZLVLOG_INFO(@"**********bugfix56118199 describe UI architecture**********\n%@\n",description);
}
打包给用户后,用户反馈,只有第一种方案有效,但为什么setTintColor
方法才会渲染按钮?alertController.view.tintColor
本身不就有值吗?
再查看用户的alertView的UI层级log,对比正常的alertView,发现一处异常:
//出bug的alertView层级
<_UIAlertControllerView: 0x11357c270; frame = (10 378.5; 300 179.5); tintColor = UIExtendedSRGBColorSpace 0 0.25098 0.866667 1; layer = <CALayer: 0x174a3dbc0>>
……
<_UIAlertControllerActionView: 0x11422e010; frame = (0 0; 300 57); Action = <UIAlertAction: 0x17030c600 Title = "�照" Descriptive = "(null)" Image = 0x0>>
| | | | | | | | | <UIView: 0x114237800; frame = (129.5 0; 41 57); userInteractionEnabled = NO; layer = <CALayer: 0x170c34500>>
| | | | | | | | | | <UILabel: 0x11422c5d0; frame = (0 16.5; 41 24); text = '�照'; userInteractionEnabled = NO; tintColor = UIExtendedSRGBColorSpace 0 0.200784 0.693333 1; layer = <_UILabelLayer: 0x170484e20>>
//正常的alertView层级
<_UIAlertControllerView: 0x11357f2e0; frame = (10 378.5; 300 179.5); layer = <CALayer: 0x174c2da80>>
<_UIAlertControllerActionView: 0x11357d8e0; frame = (0 0; 300 57); Action = <UIAlertAction: 0x174315600 Title = "�照" Descriptive = "(null)" Image = 0x0>>
| | | | | | | | | <UIView: 0x11358b2c0; frame = (129.5 0; 41 57); userInteractionEnabled = NO; layer = <CALayer: 0x174a21880>>
| | | | | | | | | | <UILabel: 0x11358b460; frame = (0 16.5; 41 24); text = '�照'; userInteractionEnabled = NO; tintColor = UIExtendedSRGBColorSpace 0 0 0 0; layer = <_UILabelLayer: 0x17448cfd0>>
可以看到tintColor = UIExtendedSRGBColorSpace 0 0 0 0
,alertView的按钮Label的tintColor是RGBA(0,0,0,0),alpha值是0,所以才没有显示。原来alertController.view.tintColor
和alertController.alertControllerActionView.label.tintColor
不是一个值!这就解释了alertController.view.tintColor
有值但按钮不显示的现象。
确认猜想,查原因
现在确认了是tintColor
的原因,接下来就开始查alertControllerActionView.label.tintColor
在哪被改变了,可是alertControllerActionView
是系统控件的私有接口,无法获取,更别说改变了。
一筹莫展的时候,看到了这个bug报告,其中说道:
When an application window's tintColor is set to white, any UIAlertController launched bears the same tintColor even when the instance of the UIAlertContoller is set to something else
由这个线索追查到,有问题的alertView都是在一个新建的UIWindow
被弹出,弹出代码如下:
//新建alertController
//……
//新建一个Window
- (UIWindow *)alertWindow
{
if (!_alertWindow) {
_alertWindow = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
_alertWindow.rootViewController = [QZLVForbidAutoRotateController new];
_alertWindow.windowLevel = NSUIntegerMax;
_alertWindow.hidden = NO;
_alertWindow.userInteractionEnabled = YES;
_alertWindow.tintColor = [[UIWindow valueForKey:@"keyWindow"] tintColor];
}
return _alertWindow;
}
//弹出alertController
[[self alertWindow].rootViewController presentViewController:alertController animated:YES completion:nil];
在本地将_alertWindow.tintColor
设为RGBA(0,0,0,0)后,复现了这个bug。同时再一次补log打包给用户,确认了这个原因。
![alertActionSheet](http://km.oa.com/files/photos/pictures/201701/1483875769_64_w540_h552.png)
![alertView](http://km.oa.com/files/photos/pictures/201701/1483875789_35_w540_h542.png)
原来是在代码的其他地方,修改了keyWindow
的tintColor为RGBA(0,0,0,0),导致了_alertWindow.tintColor
被赋值为了透明色。而苹果的默认实现中,将alertController
的按钮Label(alertControllerActionView.label.tintColor)
和弹出它的window.tintColor
相等,导致了这个bug。
总结
看来苹果关于UIAlertController
的实现是有坑的,除非手动设置alertController.view.tintColor
,否则alertControllerActionView.label.tintColor
与presenter的window.tintColor
是相关联的,让人有点摸不到头脑。不过测试了同样的代码,使用UIAlertView
就不会有这个bug。
以后使用UIAlertController
时,这一点要多多注意了。
这次这个bug时间比较紧急,leader、导师、同事们给了我很多建议和指导,看来查bug方法论还是很重要的。
网友评论