聊聊一个关于UIAlertController的“熊猫”Bug

“熊猫”Bug是指一种极难出现、难以摸索原因的bug。


今天产品在众测平台上发现了一个“熊猫”bug,这个bug的表现很奇怪,弹出的UIAlertController的按钮没有文字,但title和message有文字。这个bug我们无法重现,在外网只有两个用户的手机上出现,出现之后就是必现的。

alertActionSheet
alertView

这个bug刚拿到手有点茫然,不明白为什么使用系统的弹窗会有这种问题,一度怀疑是苹果的Bug。

建立猜想

由于这个bug只有外网的两个用户能重现,所以初步的方案是先补一些log, 增加一些实验的代码,再打个包让用户去试,看看能否查得原因。
由这个bug的变现,初步猜想这个bug可能成因:

  1. 没有在主线程弹出alertController(看用户的log排除了)
  2. 按钮的字是白色的(这个自己实验就排除了)
  3. 弹出时有其他动画冲突,导致没有渲染,需要手动调用setNeedsDisplay
  4. 按钮layout时出了问题
  5. 系统的bug(bug只在Qzone出现)
  6. 需要手动设一下tintColor

根据以上的猜想,总结出了3个解决方案,打包给用户实验:

  1. 弹出前手动设一下tintColor
  2. 弹出前手动调用setNeedsDisplay
  3. 什么也不做,排除环境影响

同时在弹出时,把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.tintColoralertController.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
alertActionSheet

alertView
alertView

原来是在代码的其他地方,修改了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方法论还是很重要的。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 218,546评论 6 507
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,224评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,911评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,737评论 1 294
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,753评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,598评论 1 305
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,338评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,249评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,696评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,888评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,013评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,731评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,348评论 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,929评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,048评论 1 270
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,203评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,960评论 2 355

推荐阅读更多精彩内容

  • 发现 关注 消息 iOS 第三方库、插件、知名博客总结 作者大灰狼的小绵羊哥哥关注 2017.06.26 09:4...
    肇东周阅读 12,105评论 4 62
  • { 11、核心动画 需要签协议,但是系统帮签好 一、CABasicAnimation 1、创建基础动画对象 CAB...
    CYC666阅读 1,547评论 2 4
  • 大纲 一、UIAlertView项目:UIAlertView0314步骤: 1.创建AlertView 2.实现可...
    AsaGuo阅读 466评论 0 0
  • 重返身邊,不代表重修舊好, 唯命是從,不需要唯唯諾諾, 選擇拋棄,是附送了灰與冷, 再次拾回,也不知是光或暗, 何...
    阿帕樹阅读 164评论 0 2
  • “阅读虽然不能改变人的命运,但是能让你成为自己。”我是在看到这句话后才觉得阅读的可贵,以前并未觉得那是一件多么了不...
    张泊宁阅读 760评论 0 2