bugly怎么读_腾讯Bugly巨坑:使用不当造成UI界面卡死

本文由CocoaChina网友gagaga投稿

前言

Bugly和dispatch_once使用不当,会造成UI界面卡死。笔者在前段时间碰见了这样的一个卡死的情况,特意记录下来。

iOS开发者或多或少都听过或用过Bugly。它是腾讯开发的一个SDK,用来捕捉App中的crash。对于dispatch_once大家就更熟悉了,现在大部分开发者用这个来创建单例。如:+ (SingletonA *)sharedInstance {

static SingletonA *_singleton = nil;

static dispatch_once_t once;

dispatch_once(&once, ^{

_singleton = [[SingletonA alloc] init];

});

return _singleton;

}

但是这两个在一起怎么会造成UI界面卡死呢?如果笔者不是亲眼所见,也不会相信Bugly会造成界面卡死。

现象

前几天碰见了这样一个情况,我们的App启动时有时候会卡在启动界面上,过一段时间 就会被系统杀掉,而且不会有Crash的堆栈。这个现象让我们开发很头疼,一旦出现就只能杀进程,重新启动App,并且还不知道是怎么回事。

调查

看到界面卡死的第一反应就是,是不是哪个地方死锁导致主线程阻塞了。使用Console.app查看App启动时的日志,没发现什么异常的情况,并且死锁这个在日志中查找起来比较麻烦。

好不容易复现这个情况后,赶紧把手机接上Mac,在Xcdoe中Attach我们App的进程,如图:

然后暂停下App进程,就可以看到当前所有线程的堆栈情况了。如图:

这下,我们才知道是卡在了dispatch_once这个地方。是我们的单例使用有问题吗?我们知道, 如果dispatch_once递归调用就会产生死锁。示例代码如下:+ (SingletonA *)sharedInstance {

static SingletonA *_singleton = nil;

static dispatch_once_t once;

dispatch_once(&once, ^{

_singleton = [[SingletonA alloc] init];

});

return _singleton;

}

- (instancetype)init {

self = [super init];

if (self) {

[self somethingInit];

// 这个方法里也调用了[SingletonA sharedInstance],所以会产生死锁

}

return self;

}

4

- (void)somethingInit {

[SingletonA sharedInstance];

}

很有可能就是这个原因导致我们的App启动时卡死。于是我们开始排查dispatch_once的代码里会不会在某个条件下再次调用到相同的dispatch_once,形成递归调用,导致死锁。

就这样折腾了好久,也没有发现dispatch_once形成递归调用的可能性,就在调查快陷入僵局的时候,笔者在日志中发现了一些信息:*** Assertion failure in -[XXXXXManager XXXXXmethod], /Users/whf/Desktop/WHF/XXXXX/XXXXX/XXXXXManager.m:32

怎么会有NSAssert断言日志信息,有NSAssert断言时App不应该早就Crash了吗,为什么会卡住被系统结束运行?笔者把NSAssert断言的地方做了处理,不会再触发断言了,然后重新Debug安装App后试了很多次,果然就不会再卡住了。

到此,启动卡住的触发原因找到了,是因为dispatch_once中的代码有NSAssert断言的Crash,导致主线程卡住。

但是,为什么会这样,笔者是一个喜欢刨根问底的人,于是有了下面的问题复现。

问题复现dispatch_once中执行的代码有Crash的话,会造成死锁吗?难道是NSAssert使用的@try{}@catch{}的异常机制改变了代码的执行顺序,致使dispatch_once死锁?

这个解释太牵强,感觉站不住脚。

笔者新建了一个demo工程,来测试这种情况。代码大致如下:

SingletonA.m 文件:@implementation SingletonA

+ (SingletonA *)sharedInstance {

static SingletonA *_singleton = nil;

static dispatch_once_t once;

dispatch_once(&once, ^{

_singleton = [[SingletonA alloc] init];

});

return _singleton;

}

- (instancetype)init {

self = [super init];

if (self) {

[self somethingInit];

}

return self;

}

- (void)somethingInit {

NSAssert(NO, @"not support message type");

}

@end

使用这个单例的地方MainViewController.m :@implementation MainViewController

- (void)viewDidLoad {

[super viewDidLoad];

[SingletonA sharedInstance];

}

@end

代码跑起来后,并没有死锁。难道是需要多线程访问[SingletonA sharedInstance]这个方法?

立马修改了下使用这个单例的地方- (void)viewDidLoad {

[super viewDidLoad];

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{

[SingletonA sharedInstance];

});

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{

[SingletonA sharedInstance];

});

[SingletonA sharedInstance];

}

在全局队列里也访问了这个单例,然而,并没有出现死锁的情况。 太令人失望了,看来,在dispatch_once执行的代码中有crash,并不会造成dispatch_once死锁。

正当陷入僵局的时候,再次看了下当时我们App卡死时后的堆栈。如图:

发现了Bugly的身影,笔者立刻在Demo工程中加入了Bugly库,然后重新跑了下代码,还是没有死锁啊,怎么回事? 又仔细端详了上面这张图,这次有新的发现。Bugly是在非主线程捕获到的这次Crash,而主线程也访问了这个出问题的单例,会不会是因为后台线程初始化这个单例的时候crash了,Bugly捕获了这个Crash在这个后台线程处理,然后主线程访问这个单例就在一直等待这个单例初始化完成。好了,笔者改了下调用单例的地方,让主线程休眠1s,方便后台线程提前初始化单例。- (void)viewDidLoad {

[super viewDidLoad];

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{

[SingletonA sharedInstance];

});

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{

[SingletonA sharedInstance];

});

sleep(1); // 让后台线程先去初始化SingletonA

[SingletonA sharedInstance];

}

果然,死锁了!!!

主线程截图:

后台线程截图:

现在明白了,使用dispatch_once的单例初始化的时候抛出异常或者Crash时,Bugly捕获到后进行处理,这时候如果主线程再试图去访问这个单例,就会造成死锁。

总结

Bugly和dispatch_once在以下条件下造成会死锁:后台线程初始化dispatch_once单例时抛出异常或者crash,

主线程也正在访问这个单例。

为什么说这个是Bugly的巨坑呢?

这种情况下正常的流程就应该Crash了,但是Bugly把这种错误变为了死锁,掩盖了问题,最后被系统杀掉,没有了Crash堆栈。这样就不好查找定位问题了,尤其是当这个Crash不是必现,而且还是线上版本的时候,就更不容易排查问题了。Bugly把我们活生生地往深沟里带,让我们最开始排查问题的方向就错了。

所以,如果集成了Bugly,App又经常不明情况的卡死可以排查下这类情况。

如何避免?

虽说是Bugly造成的死锁,但其根本原因还是我们自己代码Crash了,Bugly只是掩盖住了这种Crash,让我们不容易排查问题。那要如何避免呢?项目少用单例。笔者一直不太喜欢什么东西都搞个单例来处理。

dispatch_once中的代码尽量只做初始化的事情,不要调用很多其他的方法。

dispatch_once中的代码尽量不要抛出异常,不要Crash。

给Bugly报Bug。

好了,总结完了,成功把自己代码的问题甩锅给Bugly~~~~

请大家轻拍~~~

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/369931.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

复习HTML CSS(5)

n <meta>标记 <meta>的主要作用&#xff0c;是提供网页的源信息。比如&#xff1a;指定网页的搜索关键字 <meta>标记有两个属性&#xff1a;http-equiv和name。 1、 http-equiv属性 功能&#xff1a;模拟http协议文件头信息&#xff0c;当信息从服务器端传…

『cs231n』卷积神经网络工程实践技巧_下

概述 计算加速 方法一&#xff1a; 由于计算机计算矩阵乘法速度非常快&#xff0c;所以这是一个虽然提高内存消耗但是计算速度显著上升的方法&#xff0c;把feature map中的感受野&#xff08;包含重叠的部分&#xff0c;所以会加大内存消耗&#xff09;和卷积核全部拉伸成为向…

Spring属性占位符配置器–一些不太明显的选项

Spring的PropertySourcesPlaceholderConfigurer用于从XML或Java Config中定义的Spring bean定义外部化属性。 PlaceholderConfigurer支持的一些选项在文档中并不明显&#xff0c;但是很有趣并且可能有用。 首先&#xff0c;以Spring文档中的示例为例&#xff0c;考虑一个属性文…

计算机扬天m400c联想,【联想扬天M参数】联想扬天M系列台式电脑参数-ZOL中关村在线...

CPU型号内存容量硬盘容量屏幕尺寸显卡类型 价格详细对比Intel 奔腾双核 E52002GB DDRII 667MHz250GB SATAII 7200转高速防震硬盘集成显卡对比Intel 奔腾4 631512MBGB 512MB DDRII53380GB 7200转集成显卡对比Intel Atom 2301GBGB DDRII160GB 7200转高速防震硬盘集成显卡对比Inte…

SqlServer知识点

在公司天天写Sql写,存储过程,但是公司工具模板把创建的语句都写好了,只负责写里面的逻辑,久而久之,创建语句都不会写了。还有一些知识点都很模糊,平常使用的时候都不清楚,稀里糊涂的就在用。在这里整理一下。巩固复习。 一.存储过程。 1.存储过程类似编程语言的里面的函数,方法…

css3 flex 布局

今天做一个小实战&#xff0c;需要让一个登录框始终保持水平和垂直居中&#xff0c;第一个想到的就是通过定位&#xff08;要想让一个div居中&#xff0c;采用定位可以解决&#xff0c;示例&#xff09;&#xff0c; 然后开始接触flex布局&#xff0c;学完感觉真的好用&#x…

ios wkweb设置图片_iOS WKWebView的使用

WKWebView的使用前言最近项目中的UIWebView被替换为了WKWebView&#xff0c;因此来总结一下。示例Demo&#xff1a;WKWebView的使用本文将从以下几方面介绍WKWebView&#xff1a;1、WKWebView涉及的一些类2、WKWebView涉及的代理方法3、网页内容加载进度条和title的实现4、JS和…

元组的详细操作

一、创建元组 name(chinese,gansu,beijing)创建空元组name()元组中只包含一个元素时&#xff0c;需要在元素后面添加逗号消除歧义name(chinese,) 二、访问元组 元组可以使用下标索引来访问元组中的值name(chinese,gansu,beijing)假如要访问chinese则使用name[0] 三、修改元组 元…

Spring Data JDBC通用DAO实现–迄今为止最轻量的ORM

我很高兴宣布Spring Data JDBC存储库项目的第一个版本。 这个开放源代码库的目的是为基于Spring框架中 JdbcTemplate关系数据库提供通用&#xff0c;轻量且易于使用的DAO实现&#xff0c;与项目的Spring Data 框架兼容。 设计目标 轻巧&#xff0c;快速且开销低。 只有少数几个…

【Spark】SparkStreaming-加载外部配置文件

SparkStreaming-加载外部配置文件 spark加载配置文件_百度搜索Spark加载外部配置文件 - CSDN博客spark读取配置文件中的配置 - CSDN博客spark加载properties配置文件方法 - u013013024的博客 - CSDN博客Spark 官方文档&#xff08;4&#xff09;——Configuration配置 - bigbi…

对计算机财务管理的理解,计算机财务管理

计算机财务管理1、引用(P4-P5)&#xff1b;易出名词解释、选择题。应识记、理解。答&#xff1a;(1)相对引用(2)绝对引用(3)混合引用2、IF、SUM、VLOOKUP、DGET函数的使用方法(P22、P28、P30、P31) 识记、理解用途&#xff0c;能应用。答&#xff1a;IF函数用逻辑表达式来判断指…

卡盟主站搭建_搭建卡盟主站下载|搭建卡盟主站教程 (附带源码)百度云_ - 极光下载站...

最近&#xff0c;很多小伙伴们都在想搭建一个卡盟的主站&#xff0c;但是鉴于很多人都不太懂编程&#xff0c;也不知道如何找到源码。所以现在小编就为大家带来搭建卡盟主站教程&#xff0c;而且还把源码也一起送来了&#xff0c;想要搭建卡盟主站的话就记得一定要下载哦&#…

【期望DP】

【总览】 【期望dp】 求解达到某一目标的期望花费&#xff1a;因为最终的花费无从知晓&#xff08;不可能从$\infty$推起&#xff09;&#xff0c;所以期望dp需要倒序求解。 设$f[i][j]$表示在$(i, j)$这个状态实现目标的期望值&#xff08;相当于是差距是多少&#xff09;。 首…

复习HTML CSS(2)

n 项目符号嵌套编号思路 标签的内容&#xff08;文本、项目符号、表格、图片等&#xff09;必须放在最底层标记中。 n 图片标记&#xff08;行内元素&#xff0c;单边标记&#xff09; l 语法&#xff1a;<img 属性 “值”> l 常用属性 Width&#xff1a;图片宽…

Spring MVC:使用基于Java的配置创建一个简单的Controller

这是我博客上与Spring MVC相关的第一篇文章。 开端总是令人兴奋的&#xff0c;因此我将尽量简洁明了。 Spring MVC允许以最方便&#xff0c;直接和快速的方式创建Web应用程序。 开始使用这项技术意味着需要Spring CORE的知识。 在文章中&#xff0c;您将了解有关创建简单的Spri…

中鸣循迹机器人_自动循迹机器人控制系统的设计

马家庆,于兆勤,刘建群,黄惠敬,陈炜楠摘要&#xff1a;循迹机器人是智能机器人领域内非常重要且被广泛研究的一种智能移动装置&#xff0c;国内许多重要的比赛都以循迹机器人为核心进行开展的。本文设计的智能循迹避障机器人的控制系统主要由四个模块组成&#xff1a;最小系统模…

2、创建分类器笔记

创建分类器 简介&#xff1a;分类是指利用数据的特性将其分类成若干类型的过程。分类与回归不同&#xff0c;回归的输出是实数。监督学习分类器就是用带标记的训练数 据建立一个模型&#xff0c;然后对未知的数据进行分类。分类器可以实现分类功能的任意算法&#xff0c;最简单…

天刀现在最新服务器,天涯明月刀8.21服务器更新公告

原标题&#xff1a;天涯明月刀8.21服务器更新公告青龙乱舞区、大地飞鹰区、天命风流区、沧海云帆区、边城浪子区全部服务器将在8月21日6:00~12:00停机维护更新&#xff0c;维护完成后上述各服务器客户端版本更新至2.0.95.5。本次维护主要调整和修改内容如下&#xff0c;给您带来…

复习上学期的HTML CSS(1)

自己跟着网上教程复习上学期的HTML CSS&#xff0c;因为已经忘得差不多了&#xff0c;而且现在学的js也要以HTML CSS为基础&#xff0c;坚持每天持续更新。 n B/S 网络结构 Browser/Server 浏览器/服务器&#xff0c;这是现在最流行的网络模式。如新浪网、凤凰网等。 C/S Clie…

XAML或JavaFx?

这是使用XAML和JavaFx构建应用程序的快速&#xff0c;主观&#xff0c; 无权且非常不科学的比较。 比较是基于我与每个人合作的个人经验。 在XAML方面&#xff0c;这意味着WPF和Win8存储应用程序 。 在JavaFx方面&#xff0c;这意味着Windows 7应用程序。 JavaFX JavaFx被Orac…