当前位置: 首页
编程语言
iOS异常与信号使用场景详解

iOS异常与信号使用场景详解

热心网友 时间:2026-06-14
转载

iOS崩溃源于未处理的信号(Mach异常、Unix信号、NSException),异常类型固定为SIGABRT,分析关键在LastExceptionBacktrace。特殊异常码如0xdead10cc表示死锁,0x8badf00d表示看门狗超时。常见信号有SIGABRT、SIGSEGV等,原因涉及内存越界、野指针及多线程问题。

正文

在iOS开发中,Crash分析一直是稳定攻坚的重头戏。说白了,应用崩溃的根源就是收到了未处理的信号。这些信号来源主要有三个:系统内核、其他进程,以及应用本身。由此,我们也把异常分成了三大类。

  • Mach异常:这是底层内核级的异常。对于用户态的开发者,可以通过Mach API设置线程、任务或主机的异常端口来直接捕获这类异常。
  • Unix信号(BSD信号):如果Mach异常没有被开发者捕获,系统会在host层通过ux_exception()将其转换成对应的UNIX信号,再经由threadsignal()投递到出错的线程。我们可以调用signal(x, SignalHandler)来拦截它。
  • NSException:应用级的异常,通常指那些没有被捕获的Objective-C异常,会导致程序给自己发送SIGABRT信号而崩溃。这类异常可以通过try-catch或者NSSetUncaughtExceptionHandler()机制来捕获。

异常

Exception Type:

异常的type基本上固定是SIGABRT。这其实是CrashReporter捕获到异常之后,主动调用abort()发出的信号。正是因为这种机制,如果是Exception导致的崩溃,关键的分析信息在Last Exception Backtrace:这里,而Crash Thread里固定的只是handleException的堆栈,基本没有查看的价值。

Exception Codes:

常见的格式是 0 at 0x18ac2378,后面的地址就是发生异常的对象所处的内存地址。

特殊的 Exception Code

  • 0xdead10cc - Deaklock(死锁)

这个崩溃码意味着我们在应用挂起之前,还持有着文件锁或者SQLite数据库锁。正确的做法是在挂起之前主动释放掉这些锁。

  • 0xbaaaaaad - Bad

当用户同时按下侧边按钮和两个音量按钮,对整个系统进行stackshot时会触发这个异常码。

  • 0xbad22222 - Bad too (two) many times

这很可能是VOIP应用被频繁唤起导致的。另一个需要留意的点是后台网络调用,如果TCP连接被唤醒的次数太多(比如300秒内被唤醒15次),同样会触发此类崩溃。

  • 0x8badf00d - Ate (eight) bad food

应用程序在执行状态变更(如启动、关闭、处理系统消息等)时耗时太长,与系统的看门狗计时策略冲突(超时)而被终止。最常见的原因就是在主线程上进行同步网络请求。

  • 0xc00010ff - Cool Off

系统检测到设备发烫,主动终止了我们的App。如果只在个别设备上出现,那大概率是硬件问题,而非App自身的bug。但如果涉及设备范围较广,就需要用Instruments去排查App的耗电量了。

  • 0x2bad45ec - Too bad for security

触发了安全冲突。如果Termination Description显示为“Process detected doing insecure drawing while in secure mode”,说明应用在无权限的情况下尝试绘制内容,比如在锁屏状态下。

Triggered by Thread:

即发生Crash的线程。

Application Specific Infomation:

这里包含异常的具体信息,是定位问题的关键所在。

Last Exception Backtrace:

这是抛出异常时的代码堆栈。如果是异常崩溃,分析这个堆栈就行了。

主要信号

常见的系统信号包括:SIGTERMSIGABRTSIGSEGVSIGBUSSIGILLSIGFPTSIGKILLSIGTRAP

程序结束信号(SIGTERM)和SIGKILL的区别在于它可被阻塞和处理,常用来要求程序主动退出。但在iOS环境下,一般不太会直接处理到它。

SIGABRT原因

  • 重复释放(double free)指针。比如分配内存后连续调用两次free()
  • 释放未初始化或错误的地址。例如直接释放一个野地址。
  • 内存越界。比如将一个超长字符串strcpy到一个长度不够的数组里。
  • 直接调用abort()assert()

典型场景

全局变量的赋值代码段被多线程同时调用。上一次赋的值可能在多个线程中同时被释放,从而引发Crash。

解决方案

思路很直接:要么去掉这类赋值操作,要么老老实实加锁。

SIGSEGV原因

  • 无效的内存访问(段错误)。
  • 野指针访问,访问了没有权限的内存地址或系统内存地址。
  • 非ARC模式下,经常出现Delegate对象的野指针访问。
  • ARC模式下,Block代码块内强持有可能已被释放的对象。

典型场景

SDWebImageDownloaderOperation的生命周期管理和错误回调分布在两个不同的队列中。有可能self已经进入释放流程,此时再去访问self.completeBlock,就变成了访问无效内存。

根本原因

多线程访问或操作对象、栈溢出。

SIGBUS原因

  • mmap内存映射的访问超出了大小。
  • 访问了未对齐的内存地址。
char *p, tmp;
NSString *path = [[NSBundle mainBundle] pathForResource:@"test" ofType:@"txt"];
int fd = open(path.UTF8String, O_RDWR);
p = (char*)mmap(NULL,FILESIZE, PROT_READ|PROT_WRITE,MAP_SHARED, fd, 0); signal(SIGBUS, handle_sigbus);
getchar();
for(int i=0;i
  • int pi = (int)(0x00001111); *pi = 17;

典型场景

mmap映射文件内存后,写入时越界。

对比理解

SIGSEGV是访问了无效内存——这块内存不属于我们进程,或者我们没有权限;而SIGBUS则是CPU无法操作该地址,大部分情况是因为地址没有对齐。

SIGILL原因

  • 执行了非法指令。
  • 堆栈溢出。

典型场景

在iOS上,这个问题有可能随机出现在任何动态库或静态库的方法中,且一旦出现,应用会持续崩溃。

排查思路

App级别的代码没有修改可执行段的权限,一般无法污染代码段。这类情况通常被归因为苹果底的增量问题,重启手机往往可以解决。

SIGKILL原因

程序接收中止信号,不可被捕获或忽略。OOM、Watchdog最终都会映射为这个异常信号。常见场景包括:

  • 长时间占用过多CPU资源被系统杀掉。
  • 应用启动时在主线程做长时间操作或卡死,被Watchdog杀死。
  • 线程切换过于频繁。
  • 占用过多内存被Jetsam杀死。

SIGTRAP原因

系统库如WebKit、libdispatch等常用__builtin_trap()来触发断点异常。在Debug模式下,这会触发调试器断点,方便实时查看问题;Release模式下,应用直接崩溃,产生SIGTRAP信号。

典型场景

dispatch_group_enterdispatch_group_lea ve调用不匹配。比如dispatch_group_lea ve多调用了一次,会触发DISPATCH_CLIENT_CRASH,内部调用__builtin_trap()制造调试陷阱。

Mach 异常

Mach异常的好处在于能捕获更多类型的Crash,比如循环递归导致的堆栈溢出。传统的信号捕获方式会在崩溃线程里执行回调,但堆栈溢出后根本没有环境执行捕获逻辑。而Mach异常捕获可以定义单独的线程来处理异常逻辑。

怎么区分看到的日志是不是Mach异常?很简单:如果Exception Type:是以EXC_打头的,那就是Mach异常。后面的Exception Subtype:通常是根据Exception Type:转译过来的。

Exception Type:

  • EXC_BAD_ACCESS:内存不能访问,对应SIGBUSSIGSEGV
  • EXC_BAD_INSTRUCTION:非法指令,对应SIGILL
  • EXC_ARITHMETIC:算术运算错误,对应SIGFPE
  • EXC_EMULATION:对应SIGEMT
  • EXC_SOFTWARE:软件错误,对应SIGSYSSIGPIPESIGABRTSIGKILL
  • EXC_BREAKPOINT:对应SIGTRAP
  • EXC_SYSCALLEXC_MACH_SYSCALLEXC_RPC_ALERT:不常用。
  • EXC_CRASH:对应SIGBART
  • EXC_GUARD:一般是文件句柄防护,比如close了一个内核的fd。
  • EXC_RESOURCE:遇到了一些系统资源限制,通常是CPU过载或线程调度太频繁,比如iOS中子线程每秒唤醒次数不能超过150。

Abort

Abort 包含哪些场景?

  • 内存使用量过高、短时间内申请大量内存,系统发送信号9(无法通过信号捕获)强制杀死进程(即常说的Jetsam事件)。
  • 主线程发生卡死超过一定时间,被Watchdog强制杀死(不同系统版本超时时间不同)。
  • 启动超时、后台切前台resume超时。
  • 部分死循环、递归等造成的栈溢出。

Abort目标

  • 完整捕获现场及上下文信息。
  • 精准定位Abort的业务场景和发生原因。
  • 基于捕获的现场数据和原因,构建高效的线上分析工具链,快速定位线上崩溃率的主要成因。

Abort推导规则

需要根据可能导致客户端崩溃的原因设计推导规则,并基于线上用户的Abort数据快速聚合,这样才能及时发现并解决影响线上稳定性的大问题。

来源:https://www.jb51.net/article/268366.htm

游乐网为非赢利性网站,所展示的游戏/软件/文章内容均来自于互联网或第三方用户上传分享,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系youleyoucom@outlook.com。

同类文章
更多
AWS RDS 数据库配置入门与基础操作指南

AWS RDS 数据库配置入门与基础操作指南

本文介绍了AWSRDS的基本概念与核心价值,即提供托管式关系数据库服务,简化运维。详细阐述了创建RDS实例的关键配置步骤,包括引擎选择、实例规格、存储与网络设置。最后,指导读者如何通过多种方式安全连接至数据库实例,并开始进行数据操作,为后续应用开发奠定基础。

时间:2026-07-10 06:41
PHP MVC中AJAX请求无法调用控制器方法的原因与解决方案

PHP MVC中AJAX请求无法调用控制器方法的原因与解决方案

PHPMVC中AJAX请求返回整页HTML的常见原因是控制器方法未正确输出响应或未终止执行,导致框架渲染视图。解决方法是在控制器中设置JSON响应头、输出数据后调用exit()明确终止,同时前端使用小写url和dataType: "json "。

时间:2026-07-10 06:40
Go语言手动构造rsa.PublicKey:正确初始化大整数模数N完整指南

Go语言手动构造rsa.PublicKey:正确初始化大整数模数N完整指南

手动构造RSA公钥时,模数N为*big Int类型,不能直接使用超长十进制字面量,需通过SetString或UnmarshalText方法解析字符串。公钥指数E可直接赋值,推荐65537。生产环境应使用rsa GenerateKey生成密钥对,避免手动构造引发的安全和格式错误。

时间:2026-07-10 06:39
Go语言实现HTTP定时轮询监控多URL响应时间与状态检测

Go语言实现HTTP定时轮询监控多URL响应时间与状态检测

使用Go语言实现HTTP定时轮询监控,通过按行分割与Tab解析URL列表,避免闭包陷阱和nil指针,每个URL启动独立ticker安全并发请求,并配置超时控制与资源关闭,确保响应时间与状态码准确检测。

时间:2026-07-10 06:39
Tkinter中Label标签在主循环动态更新的正确方法

Tkinter中Label标签在主循环动态更新的正确方法

在Tkinter中正确动态更新标签的方法:将标签组件的textvariable参数绑定到一个StringVar变量,然后通过调用该变量的 set()方法更新其值,界面会自动刷新。这样避免直接修改text属性或调用update()。此做法实现数据与界面的解耦,代码更简洁,响应更及时,避免手动同步的闪烁,推荐做法。

时间:2026-07-10 06:39
热门专题
更多
刀塔传奇破解版无限钻石下载大全 刀塔传奇破解版无限钻石下载大全
洛克王国正式正版手游下载安装大全 洛克王国正式正版手游下载安装大全
思美人手游下载专区 思美人手游下载专区
好玩的阿拉德之怒游戏下载合集 好玩的阿拉德之怒游戏下载合集
不思议迷宫手游下载合集 不思议迷宫手游下载合集
百宝袋汉化组游戏最新合集 百宝袋汉化组游戏最新合集
jsk游戏合集30款游戏大全 jsk游戏合集30款游戏大全
宾果消消消原版下载大全 宾果消消消原版下载大全
  • 热门数据榜