iOS_Crash 四:的捕获和防护
文章目录
- 1.Crash 捕获
- 1.2.NSException
- 1.2.C++异常
- 1.3.Mach异常
- 1.4.Unix 信号
- 2.Crash 防护
- 2.1.方法未实现
- 2.2.KVC 导致 crash
- 2.3.KVO 导致 crash
- 2.4.集合类导致 crash
- 2.5.其他需要注意场景:
1.Crash 捕获
根据 Crash
的不同来源,分为以下三类:
1.2.NSException
应用层的异常,未被捕获的异常,导致程序向自身发送了 SIGABRT
信号而崩溃,是应用程序自己可控的。对于未被捕获的异常,是可以通过 try-catch
或 NSSetUncaughtExceptionHandler()
机制类捕获的。
常见的 Exception:
NSInvalidArgumentException
:非法参数异常。加强对参数的检查,避免传入非法参数,特别是标记为 nonull 的参数。NSRangeException
:越界异常NSGenericException
:遍历的同时对原集合进行修改NSInternalInconsistencyException
:不一致异常。如NSDictionary
当NSMutableNSDictionary
使用。NSFileHandleOperationException
:文件处理异常。常见的是存储空间不足NSMallocException
:内存异常。如内存不足。
系统定义的所有Exception
见 NSExceptionName
捕获 NSExpection:
// 记录之前的Crash回调函数(如果有的话)
static NSUncaughtExceptionHandler *previousUncaughtExceptionHandler = NULL;+ (void)registerUncaughtExceptionHandler {// 将别人之前注册的Crash回调取出并备份previousUncaughtExceptionHandler = NSGetUncaughtExceptionHandler();// 然后再注册自己的NSSetUncaughtExceptionHandler(&UncaughtExceptionHandler);
}// 崩溃时的回调函数
static void UncaughtExceptionHandler(NSException * exception) {// 异常的堆栈信息NSArray *stackInfo = [exception callStackSymbols];// 出现异常的原因NSString *reason = [exception reason];// 异常名称NSString *name = [exception name];// 异常错误报告NSString *exceptionInfo = [NSString stringWithFormat:@"uncaughtException异常错误报告:\n name:%@\n reason:\n %@\n callStackSymbols:\n %@", name, reason, [stackInfo componentsJoinedByString:@"\n"]];// 保存Crash日志到沙盒cache目录[SKTool cacheCrashLog:exceptionInfo name:@"CrashLog(UncaughtException)"];// 在自己handler处理完后记得把别人的handler注册回去,形成规范的SOPif (previousUncaughtExceptionHandler) {previousUncaughtExceptionHandler(exception);}// 杀掉程序,这样可以防止同时抛出的SIGABRT被Signal异常捕获kill(getpid(), SIGKILL);
}
1.2.C++异常
系统捕获到 C++ 异常后会将其转换为 OC 异常抛出,此时的调用堆栈是在异常发生时的队长;但若转换失败则会调用 __cxa_throw
抛出异常,此时的调用队长是处理异常的堆栈,导致原始异常调用堆栈丢失。
捕获 C++ 异常:
- 设置异常处理函数:
g_originalTerminateHandler = std::set_terminate(CPPExceptionTerminate);
调用 set_terminate(CPPExceptionTerminate)
设置新的全局终止处理函数并保持旧的函数。
- 重写
__cxa_throw
:
void __cxa_throw(void* thrown_exception, std::type_info* tinfo, void (*dest)(void*)) {// 获取调用堆栈并存储// 再调用原始的 __cxa_throw 函数
}
- 异常处理函数
__cxa_throw
往后执行,进入set_terminate
设置的异常梳理函数。判断如果是 OC 异常则什么也不多,让 OC 异常机制处理;否则获取异常信息。
1.3.Mach异常
内核层的异常。用户态开发者可以通过 Mach API 设置 thread
、task
、hot
的异常端口来捕获 Mach 异常。
tasks
:资源所有权单位。每个任务由一个虚拟地址空间、一个端口权限名称控件、一个或多个线程组成。(类似于进程)threads
:任务中 CPU 执行的单位ports
:安全的单工通信通道,只能通过发生和接收功能进行访问。
与 Mach
异常相关的 API 有:
task_get_exception_ports
:获取 task 的异常端口task_set_exception_ports
:设置 task 的异常端口mach_port_allocate
:创建调用者指定的端口权限类型mach_port_insert_right
:将指定的端口插入目标 task
注意:避免在 Xcode 联调时监听,会死锁。
1.4.Unix 信号
又称 BSD
信号,如果开发者没有捕获 Mach 异常,则会被 host 层的方法 ux_exception()
将异常转换为对应的 Unix 信号,并通过方法 threadsignal()
将信号投递到出错线程。可以同 signal(x, SignalHandler)
来捕获 signal
。
信号表:
SIGHUP
:挂起SIGINT
:程序终止信号 interrupt,在用户键入 INTR 字符(通常是 Ctrl-C)是发出,用于通知前台进程组终止进程。SIGQUIT
:程序退出信号 quit,由 QUIT 字符来控制(通常是Ctrl-),程序在收到该信号退出时会生成 core 文件。SIGILL
:执行非法指令SIGTRAP
:由断点指令或陷阱指令SIGABRT
:程序打断信号 abort。SIGBUS
:非法地址SIGFPE
:致命的算术运算错误SIGKILL
:立即结束程序的运行。不能被阻塞、处理和忽略。SIGUSR1
:用户信号1SIGSEGV
:无效内存访问SIGUSR2
:用户信号2SIGPIPE
:管道破裂。进程间的通信,如管道的异常读写。SIGALRM
:alarm 发出的信号SIGTERM
:终止信号,可被阻塞和处理。通常用来要求程序自己正常退出SIGSTKFLT
:栈溢出SIGCHLD
:子进程退出SIGCONT
:进程继续SIGSTOP
:进程停止SIGTSTP
:进程停止SIGTTIN
:进程停止,后台进程从终端读数据时SIGTTOU
:进程停止,后台进程想终端写数据时SIGURG
:I/O有紧急数据达到当前进程SIGXCPU
:进程的CPU时间篇到期SIGXFSZ
:文件大小超出上限SIGVTALRM
:虚拟时钟超时SIGPROF
:profile 时钟超时SIGWINVH
:窗口大小改变SIGIO
:I/O相关SIGPWR
:关机SIGSYS
:非法的系统调用
Tips: 在终端输入
kill -l
查看所有的 signal 信号。
捕获信号:
// 一般需要捕获的信号
static const int g_fatalSignals[] = {SIGABRT,SIGBUS,SIGFPE,SIGILL,SIGPIPE,SIGSEGV,SIGSYS,SIGTRAP,
};
void installSignalHandler() {stack_t ss;struct sigaction sa;struct timespec req, rem;long ret;// 申请一块内存空间作为可选的信号处理函数栈使用ss.ss_flags = 0;ss.ss_size = SIGSTKSZ;ss.ss_sp = malloc(ss.ss_size);// 使用 sigaltstack 函数通知系统可选的信号处理栈帧的存在及其位置sigaltstack(&ss, NULL);// 指定 SA_ONSTACK 标志通知系统这个信号处理函数应该在可选的栈帧上面执行注册的信号处理函数memset(&sa, 0, sizeof(sa));sa.sa_handler = handleSignalException;sa.sa_flags = SA_ONSTACK;sigaction(SIGABRT, &sa, NULL);
}void XXXHandleSignalException(int signal) {// 打印堆栈NSMutableString *crashInfo = [[NSMutableString alloc] init];[crashInfo appendString:[NSString stringWithFormat:@"signal:%d\n",signal]];[crashInfo appendString:@"Stack:\n"];void* callstack[128];int i, frames = backtrace(callstack, 128);char** strs = backtrace_symbols(callstack, frames);for (i = 0; i <frames; ++i) {[crashInfo appendFormat:@"%s\n", strs[I]];}NSLog(@"%@", crashInfo);// 移除其他 Crash 监听, 防止死锁NSSetUncaughtExceptionHandler(NULL);signal(SIGHUP, SIG_DFL);signal(SIGINT, SIG_DFL);signal(SIGQUIT, SIG_DFL);signal(SIGABRT, SIG_DFL);signal(SIGILL, SIG_DFL);signal(SIGSEGV, SIG_DFL);signal(SIGFPE, SIG_DFL);signal(SIGBUS, SIG_DFL);signal(SIGPIPE, SIG_DFL);
}
2.Crash 防护
2.1.方法未实现
找不到方法的实现:unrecognized selector sent to instance
,查找过程详情可见:iOS_Objective-C 消息发送(消息查找 及 消息转发)过程
解决方案:
给 NSObject
新增分类,实现消息转发
的几个方法来规避 Crash
:
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector {if ([self respondsToSelector:aSelector]) { // 已实现不做处理return [self methodSignatureForSelector:aSelector];}return [NSMethodSignature signatureWithObjCTypes:"v@:"];
}
- (void)forwardInvocation:(NSInvocation *)anInvocation {NSLog(@"%@ can't responds %@", NSStringFromClass([self class]), NSStringFromSelector(anInvocation.selector));
}
+ (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector {if ([self respondsToSelector:aSelector]) { // 已实现不做处理return [self methodSignatureForSelector:aSelector];}return [NSMethodSignature signatureWithObjCTypes:"v@:"];
}
+ (void)forwardInvocation:(NSInvocation *)anInvocation {NSLog(@"%@ can't responds %@", NSStringFromClass([self class]), NSStringFromSelector(anInvocation.selector));
}
2.2.KVC 导致 crash
KVC 的搜索模式详情可见:iOS_KVC:Key-Value Coding-2(访问者搜索模式),当最终找不到对应的key时,会导致 crash。
常见场景:
- 场景1:key 不存在
XXXClass * obj = [[XXXClass alloc] init];
[obj setValue:nil forKey:@"xxx"];
// reason: '[<XXXClass 0x2810bfa80> setValue:forUndefinedKey:]: this class is not key value coding-compliant for the key xxx.'id value = [obj valueForKey:@"xxx"];
// Thread 1: "[<MOPerson 0x600000c76c10> valueForUndefinedKey:]: this class is not key value coding-compliant for the key xxx."
- 场景2:key 为 nil
XXXClass* obj = [[XXXClass alloc] init];
[obj setValue:@"value" forKey:nil];
// reason: '*** -[XXXClass setValue:forKey:]: attempt to set a value for a nil key'// 另外:value 为 nil 不会崩溃
[obj setValue:nil forKey:@"name"];
解决方案:覆写系统会抛出异常的实现:
- (id)valueForUndefinedKey:(NSString *)key {NSLog(@"Error: valueForUndefinedKey: %@", key);return nil;
}
- (void)setValue:(id)value forUndefinedKey:(NSString *)key {NSLog(@"Error: setValue:%@ forUndefinedKey: %@", value, key);
}
2.3.KVO 导致 crash
场景:
- 观察者/被观察者 是局部变量
- 未实现
observeValueForKeyPath:ofObject:changecontext:
- 移除未注册的观察者(如:重复移除)
Tips: 重复添加观察者,不会crash,但会回调多次
解决方案:
addObserver
和removeObserver
必须成对出现- 使用 Facebook 的 KVOController 实现
2.4.集合类导致 crash
常见场景:
- 越界
NSArray *arr = [NSArray array];
id value = [arr objectAtIndex:1];
// Thread 1: "*** -[__NSArray0 objectAtIndex:]: index 1 beyond bounds for empty array"
- 塞入 nil
NSMutableArray *arr = [NSMutableArray array];
[arr addObject:nil];
// Thread 1: "*** -[__NSArrayM insertObject:atIndex:]: object cannot be nil"NSMutableDictionary *dict = [NSMutableDictionary dictionary];
[dict setObject:nil forKey:@"xxx"];
// Thread 1: "*** -[__NSDictionaryM setObject:forKey:]: object cannot be nil (key: xxx)"
解决方案:
- 使用 runtime 在这些修改方法调用前添加判空处理,详情见:Demo
2.5.其他需要注意场景:
performSelector:
必须先判断respondsToSelector:
- 调用 delegate 的方法前,必须先判断
respondsToSelector:
- id 类型不能强转,必须先判断
isKindOfClass:
- 访问 UIKit 时一定要 dispatch 到 main queue
- 一个实例,不能保证线程访问安全时,记得要加读写锁
dispatch_group_leave
比dispatch_group_enter
必须成对出现- 检查属性的修饰方式 (
assign
/strong
/weak
/copy
) - block 调用前必须判空
- 遍历结合类型对象时不要同时对其进行修改
- 耗时操作一定 dispatch 到子线程,避免触发 watchDog
Debug
模式开启僵尸模式,方便即时发现问题。- 使用
Xcode
的Address Sanitizer
检测地址访问越界
参考:
iOS Crash/崩溃/异常 捕获
Linux 信号列表
浅谈 iOS 中的 Crash 捕获与防护
iOS中常见Crash总结
相关文章:
iOS_Crash 四:的捕获和防护
文章目录 1.Crash 捕获1.2.NSException1.2.C异常1.3.Mach异常1.4.Unix 信号 2.Crash 防护2.1.方法未实现2.2.KVC 导致 crash2.3.KVO 导致 crash2.4.集合类导致 crash2.5.其他需要注意场景: 1.Crash 捕获 根据 Crash 的不同来源,分为以下三类:…...
spring boot项目运行jar包读取包内resources目录下的文件
spring boot项目运行jar包读取包内resources目录下的文件 摘要码代码相关文章 摘要 Spring Boot 项目打包成 jar 包后,resources 目录下的文件将会被打包到 jar 包中。如果需要在 Spring Boot 项目运行 jar 包后读取 resources 目录下的文件,可以使用 t…...

浙大陈越何钦铭数据结构06-图1 列出连通集
题目 给定一个有N个顶点和E条边的无向图,请用DFS和BFS分别列出其所有的连通集。假设顶点从0到N−1编号。进行搜索时,假设我们总是从编号最小的顶点出发,按编号递增的顺序访问邻接点。 输入格式: 输入第1行给出2个整数N(0<N≤10)和E&…...

C# Winform编程(9)网络编程
网络编程 HTTP网络编程IPAddress IP地址类WebClient类WebRequest类和WebResponse类 WebBrowser网页浏览器控件TCP网络编程TcpClient类TcpListener类NetworkStream类Socket类 HTTP网络编程 IPAddress IP地址类 IPAddress类代表IP地址,可在十进制表示法和实际的整数…...
RabbitMQ中方法channel.basicAck的使用说明
方法channel.basicAck的作用 在RabbitMQ中,channel.basicAck方法用于确认已经接收并处理了消息。 方法的参数说明 public void basicAck(long deliveryTag,boolean multiple) 参数: long deliveryTag 消息的唯一标识。每条消息都有自己的ID号&#x…...

Jenkins+Python自动化测试持续集成详细教程
Jenkins安装 Jenkins安装 Jenkins是一个开源的软件项目,是基于java开发的一种持续集成工具,用于监控持续重复的工作,旨在提供一个开放易用的软件平台,使软件的持续集成变成可能。由于是基于java开发因此它也依赖java环境&…...

Lightroom学习之路
基础知识 常用快捷键 双击修改图片下右边布局的属性,快速回到初始值 B站学习笔记 1、导入到图库为图片标星级,后期优先处理星级高的图片 2、修改照片-基础-白平衡有吸管吸颜色会自动平衡照片颜色 3、直方图左右上角三角形,选中后照片会显示…...
Day 2 Abp框架下,MySQL数据迁移时,添加表和字段注释
后端采用Abp框架,当前最新版本是7.4.0。 数据库使用MySQL,在执行数据库迁移时,写在Domain层的Entity类上的注释通通都没有,这样查看数据库字段的含义时,就需要对照代码来看,有些不方便。今天专门来解决这个…...

传智教育研究院重磅发布Java学科新研发《智慧养老》项目
在招聘Java开发人才的过程中,企业往往对候选人的项目经验有着严格的要求,项目经验成为顺利就业的重要敲门砖之一。而在数字化技术的学习中,如何让学员通过项目课程有效地积累实战开发经验,就成了数字化技术职业教育的一个重大难点…...

Fiddler抓包VSCode和探索
前言: 最近在使用 VSCode 调试 web 程序时,遇到一些问题,当时不知道如何是好。所以决定抓看来看一看,然后一顿操作猛如虎,成功安装了抓包软件 – Fiddler Classic。我并没有使用 Postman 这种重量级的 HTTP 测试软件&a…...

Pytorch指定数据加载器使用子进程
torch.utils.data.DataLoader(train_dataset, batch_sizebatch_size, shuffleTrue,num_workers4, pin_memoryTrue) num_workers 参数是 DataLoader 类的一个参数,它指定了数据加载器使用的子进程数量。通过增加 num_workers 的数量,可以并行地读取和预处…...

【科普】干货!带你从0了解移动机器人(六) (底盘结构类型)
牵引式移动机器人(AGV/AMR),通常由一个牵引车和一个或多个被牵引的车辆组成。牵引车是机器人的核心部分,它具有自主导航和定位功能,可以根据预先设定的路径或地标进行导航,并通过传感器和视觉系统感知周围环…...

爆肝整理,Pytest+Allure+Jenkins自动化测试集成实战(图文详细步骤)
目录:导读 前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结(尾部小惊喜) 前言 1、简介 pytesta…...

微信批量添加好友,让你的人脉迅速增长
在这个数字化时代,微信作为中国最流行的社交平台之一,已经成为了人们生活中不可或缺的一部分。它的广泛使用为我们提供了无限的社交可能性。你是否曾为了扩大人脉圈子而犯愁?今天,我将向你揭示一个高效添加微信好友的秘密武器&…...

3D模型怎么贴法线贴图?
1、法线贴图的原理? 法线贴图(normal mapping)是一种计算机图形技术,用于在低多边形模型上模拟高多边形模型的细节效果。它通过在纹理坐标上存储和应用法线向量的信息来实现。 法线贴图的原理基于光照模型。在渲染过程中&#x…...

QT中文乱码解决方案与乱码的原因
相信大家应该都遇到过中文乱码的问题,有时候改一改中文就不乱码了,但是有时候用同样的方式还是乱码,那么这个乱码到底是什么原因,又该如何彻底解决呢? 总结 先总结一下: Qt5中,将QString()的构…...

sam9x60 boot
...

3D模型格式转换工具HOOPS Exchange:支持国际标准STEP格式!
HOOPS Exchange SDK是一组C软件库,使开发团队能够快速将可靠的2D和3D CAD导入和导出添加到其应用程序中,访问广泛的数据,包括边界表示 (B-REP)、产品制造信息 (PMI)、模型树、视图、持久 ID、样式、构造几何、可视化等,无需依赖任…...

java--死循环与循环嵌套
1.死循环 可以一直执行下去的一种循环,如果没有干预不会停下来的 2.死循环的写法 3.循环嵌套 循环中又包含循环 4.循环嵌套的特点 外部循环每循环一次,内部循环会全部执行完一轮...

基于机器视觉的图像拼接算法 计算机竞赛
前言 图像拼接在实际的应用场景很广,比如无人机航拍,遥感图像等等,图像拼接是进一步做图像理解基础步骤,拼接效果的好坏直接影响接下来的工作,所以一个好的图像拼接算法非常重要。 再举一个身边的例子吧,…...
KubeSphere 容器平台高可用:环境搭建与可视化操作指南
Linux_k8s篇 欢迎来到Linux的世界,看笔记好好学多敲多打,每个人都是大神! 题目:KubeSphere 容器平台高可用:环境搭建与可视化操作指南 版本号: 1.0,0 作者: 老王要学习 日期: 2025.06.05 适用环境: Ubuntu22 文档说…...
在鸿蒙HarmonyOS 5中实现抖音风格的点赞功能
下面我将详细介绍如何使用HarmonyOS SDK在HarmonyOS 5中实现类似抖音的点赞功能,包括动画效果、数据同步和交互优化。 1. 基础点赞功能实现 1.1 创建数据模型 // VideoModel.ets export class VideoModel {id: string "";title: string ""…...

HTML 列表、表格、表单
1 列表标签 作用:布局内容排列整齐的区域 列表分类:无序列表、有序列表、定义列表。 例如: 1.1 无序列表 标签:ul 嵌套 li,ul是无序列表,li是列表条目。 注意事项: ul 标签里面只能包裹 li…...

【机器视觉】单目测距——运动结构恢复
ps:图是随便找的,为了凑个封面 前言 在前面对光流法进行进一步改进,希望将2D光流推广至3D场景流时,发现2D转3D过程中存在尺度歧义问题,需要补全摄像头拍摄图像中缺失的深度信息,否则解空间不收敛…...

ios苹果系统,js 滑动屏幕、锚定无效
现象:window.addEventListener监听touch无效,划不动屏幕,但是代码逻辑都有执行到。 scrollIntoView也无效。 原因:这是因为 iOS 的触摸事件处理机制和 touch-action: none 的设置有关。ios有太多得交互动作,从而会影响…...

【7色560页】职场可视化逻辑图高级数据分析PPT模版
7种色调职场工作汇报PPT,橙蓝、黑红、红蓝、蓝橙灰、浅蓝、浅绿、深蓝七种色调模版 【7色560页】职场可视化逻辑图高级数据分析PPT模版:职场可视化逻辑图分析PPT模版https://pan.quark.cn/s/78aeabbd92d1...

vulnyx Blogger writeup
信息收集 arp-scan nmap 获取userFlag 上web看看 一个默认的页面,gobuster扫一下目录 可以看到扫出的目录中得到了一个有价值的目录/wordpress,说明目标所使用的cms是wordpress,访问http://192.168.43.213/wordpress/然后查看源码能看到 这…...

Selenium常用函数介绍
目录 一,元素定位 1.1 cssSeector 1.2 xpath 二,操作测试对象 三,窗口 3.1 案例 3.2 窗口切换 3.3 窗口大小 3.4 屏幕截图 3.5 关闭窗口 四,弹窗 五,等待 六,导航 七,文件上传 …...
怎么让Comfyui导出的图像不包含工作流信息,
为了数据安全,让Comfyui导出的图像不包含工作流信息,导出的图像就不会拖到comfyui中加载出来工作流。 ComfyUI的目录下node.py 直接移除 pnginfo(推荐) 在 save_images 方法中,删除或注释掉所有与 metadata …...

【Post-process】【VBA】ETABS VBA FrameObj.GetNameList and write to EXCEL
ETABS API实战:导出框架元素数据到Excel 在结构工程师的日常工作中,经常需要从ETABS模型中提取框架元素信息进行后续分析。手动复制粘贴不仅耗时,还容易出错。今天我们来用简单的VBA代码实现自动化导出。 🎯 我们要实现什么? 一键点击,就能将ETABS中所有框架元素的基…...