iOS开发如何自己捕获Crash
为了在iOS中捕获和处理未捕获的Objective-C异常和系统信号引起的崩溃,可以使用`NSSetUncaughtExceptionHandler`和标准的Unix信号处理机制来实现。这能帮助你记录绝大部分的崩溃信息。以下是详细的实现步骤和代码示例:
一、系统崩溃处理
通过`NSSetUncaughtExceptionHandler`捕获未处理的Objective-C异常:
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>@interface UncaughtExceptionHandler : NSObject+ (void)installUncaughtExceptionHandler:(BOOL)install showAlert:(BOOL)showAlert;@end#import "UncaughtExceptionHandler.h"
#include <libkern/OSAtomic.h>
#include <execinfo.h>NSString * const UncaughtExceptionHandlerSignalExceptionName = @"UncaughtExceptionHandlerSignalExceptionName";
NSString * const UncaughtExceptionHandlerSignalKey = @"UncaughtExceptionHandlerSignalKey";
NSString * const UncaughtExceptionHandlerAddressesKey = @"UncaughtExceptionHandlerAddressesKey";
volatile int32_t UncaughtExceptionCount = 0;
const int32_t UncaughtExceptionMaximum = 10;
static BOOL showAlertView = nil;void HandleException(NSException *exception);
void SignalHandler(int signal);
NSString* getAppInfo(void);@interface UncaughtExceptionHandler()
@property (assign, nonatomic) BOOL dismissed;
@end@implementation UncaughtExceptionHandler+ (void)installUncaughtExceptionHandler:(BOOL)install showAlert:(BOOL)showAlert {if (install && showAlert) {[[self alloc] alertView:showAlert];}NSSetUncaughtExceptionHandler(install ? HandleException : NULL);signal(SIGABRT, install ? SignalHandler : SIG_DFL);signal(SIGILL, install ? SignalHandler : SIG_DFL);signal(SIGSEGV, install ? SignalHandler : SIG_DFL);signal(SIGFPE, install ? SignalHandler : SIG_DFL);signal(SIGBUS, install ? SignalHandler : SIG_DFL);signal(SIGPIPE, install ? SignalHandler : SIG_DFL);
}- (void)alertView:(BOOL)show {showAlertView = show;
}+ (NSArray *)backtrace {void* callstack[128];int frames = backtrace(callstack, 128);char **strs = backtrace_symbols(callstack, frames);NSMutableArray *backtrace = [NSMutableArray arrayWithCapacity:frames];for (int i = 0; i < frames; i++) {[backtrace addObject:[NSString stringWithUTF8String:strs[i]]];}free(strs);return backtrace;
}- (void)handleException:(NSException *)exception {[self validateAndSaveCriticalApplicationData:exception];if (!showAlertView) {return;}UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"出错啦"message:@"你可以尝试继续操作,但是应用可能无法正常运行."delegate:selfcancelButtonTitle:@"退出"otherButtonTitles:@"继续", nil];[alert show];CFRunLoopRef runLoop = CFRunLoopGetCurrent();CFArrayRef allModes = CFRunLoopCopyAllModes(runLoop);while (!self.dismissed) {for (NSString *mode in (__bridge NSArray *)allModes) {CFRunLoopRunInMode((CFStringRef)mode, 0.001, false);}}CFRelease(allModes);NSSetUncaughtExceptionHandler(NULL);signal(SIGABRT, SIG_DFL);signal(SIGILL, SIG_DFL);signal(SIGSEGV, SIG_DFL);signal(SIGFPE, SIG_DFL);signal(SIGBUS, SIG_DFL);signal(SIGPIPE, SIG_DFL);if ([[exception name] isEqual:UncaughtExceptionHandlerSignalExceptionName]) {kill(getpid(), [[[exception userInfo] objectForKey:UncaughtExceptionHandlerSignalKey] intValue]);} else {[exception raise];}
}- (void)alertView:(UIAlertView *)anAlertView clickedButtonAtIndex:(NSInteger)anIndex {if (anIndex == 0) {self.dismissed = YES;}
}- (void)validateAndSaveCriticalApplicationData:(NSException *)exception {NSString *exceptionInfo = [NSString stringWithFormat:@"\n--------Log Exception---------\nappInfo :\n%@\n\nexception name :%@\nexception reason :%@\nexception userInfo :%@\ncallStackSymbols :%@\n\n--------End Log Exception-----",getAppInfo(), exception.name, exception.reason, exception.userInfo ?: @"no user info", [exception callStackSymbols]];NSLog(@"%@", exceptionInfo);// 保存到文件等操作
}@endvoid HandleException(NSException *exception) {int32_t exceptionCount = OSAtomicIncrement32(&UncaughtExceptionCount);if (exceptionCount > UncaughtExceptionMaximum) {return;}NSArray *callStack = [exception callStackSymbols];NSMutableDictionary *userInfo = [NSMutableDictionary dictionaryWithDictionary:[exception userInfo]];[userInfo setObject:callStack forKey:UncaughtExceptionHandlerAddressesKey];[[[UncaughtExceptionHandler alloc] init]performSelectorOnMainThread:@selector(handleException:)withObject:[NSException exceptionWithName:[exception name] reason:[exception reason] userInfo:userInfo]waitUntilDone:YES];
}void SignalHandler(int signal) {int32_t exceptionCount = OSAtomicIncrement32(&UncaughtExceptionCount);if (exceptionCount > UncaughtExceptionMaximum) {return;}NSString* description = nil;switch (signal) {case SIGABRT:description = @"Signal SIGABRT was raised!";break;case SIGILL:description = @"Signal SIGILL was raised!";break;case SIGSEGV:description = @"Signal SIGSEGV was raised!";break;case SIGFPE:description = @"Signal SIGFPE was raised!";break;case SIGBUS:description = @"Signal SIGBUS was raised!";break;case SIGPIPE:description = @"Signal SIGPIPE was raised!";break;default:description = [NSString stringWithFormat:@"Signal %d was raised!", signal];}NSMutableDictionary *userInfo = [NSMutableDictionary dictionary];NSArray *callStack = [UncaughtExceptionHandler backtrace];[userInfo setObject:callStack forKey:UncaughtExceptionHandlerAddressesKey];[userInfo setObject:[NSNumber numberWithInt:signal] forKey:UncaughtExceptionHandlerSignalKey];[[[UncaughtExceptionHandler alloc] init]performSelectorOnMainThread:@selector(handleException:)withObject:[NSException exceptionWithName:UncaughtExceptionHandlerSignalExceptionName reason:description userInfo:userInfo]waitUntilDone:YES];
}NSString* getAppInfo() {NSString *appInfo = [NSString stringWithFormat:@"App : %@ %@(%@)\nDevice : %@\nOS Version : %@ %@\n",[[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleDisplayName"],[[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleShortVersionString"],[[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleVersion"],[UIDevice currentDevice].model,[UIDevice currentDevice].systemName,[UIDevice currentDevice].systemVersion];return appInfo;
}
在`didFinishLaunchingWithOptions`中调用`installUncaughtExceptionHandler`:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {[UncaughtExceptionHandler installUncaughtExceptionHandler:YES showAlert:YES];return YES;
}
二、处理Signal
在Xcode中测试Signal类型崩溃时,需在调试控制台输入以下命令,以允许Signal回调进入处理函数:
pro hand -p true -s false SIGABRT
三、测试代码
- (void)viewDidLoad {[super viewDidLoad];[self exceptionHandlerTest1];// [self exceptionHandlerTest2];// [self exceptionHandlerTest3];
}/// 异常处理测试1
-(void)exceptionHandlerTest1{NSArray *array= @[@"tom",@"xxx",@"ooo"];[array objectAtIndex:5];
}/// 异常处理测试2
-(void)exceptionHandlerTest2{[self performSelector:@selector(string) withObject:nil afterDelay:2.0];
}/// 异常处理测试3
-(void)exceptionHandlerTest3{int list[2]={1,2};int *p = list;free(p);p[1] = 5;
}
相关文章:
iOS开发如何自己捕获Crash
为了在iOS中捕获和处理未捕获的Objective-C异常和系统信号引起的崩溃,可以使用NSSetUncaughtExceptionHandler和标准的Unix信号处理机制来实现。这能帮助你记录绝大部分的崩溃信息。以下是详细的实现步骤和代码示例: 一、系统崩溃处理 通过NSSetUncaug…...
雪花算法(Snowflake Algorithm)
雪花算法(Snowflake Algorithm)是一种分布式唯一ID生成算法,主要用于生成全球唯一的ID,广泛应用于分布式系统中,例如在数据库中作为主键。这个算法最初由Twitter提出,并且被广泛使用在很多大规模系统中。有…...
〖任务1〗ROS2 jazzy Linux Mint 22 安装教程
前言: 本教程在Linux系统上使用。 目录 一、linux安装二、linux VPN安装三、linux anaconda安装(可选)四、linux ROS2 安装五、rosdep init/update 解决方法六、安装GUI 一、linux安装 移动硬盘安装linux:[LinuxToGo教程]把ubunt…...
图像增强:使用周围像素填充掩码区域
制作图像需要填充的掩码区域,对需要填充的位置的mask赋值非0,不需要填充赋值为0使用cv2.inpaint对图像掩码mask中非0元素位置的图像像素进行修复。从而实现使用周围像素填充掩码区域cv2.inpaint 是 OpenCV 库中的一个函数,用于图像修复(inpainting),即填充图像中的损坏区…...
给虚拟机Ubuntu扩展硬盘且不丢数据
1.Ubuntu关机状态下先扩展,如扩展20GB 2.进入ubuntu,切换root登录,必须是root全选,否则启动不了分区工具gparted 将新的20GB创建好后,选择ext4,primary; 3.永久挂载 我的主目录在/并挂载到/dev/sda1 从图…...
Oracle(41)如何使用PL/SQL批量处理数据?
在PL/SQL中,批量处理数据是一种高效的方法,可以在数据库中处理大量数据,而无需逐行操作。批量处理数据的关键技术包括: PL/SQL表(索引表):在内存中存储数据以进行批量操作。FORALL语句…...
JavaEE 第2节 线程安全知识铺垫1
目录 一、通过jconsole.exe查看线程状态的方法 二、Thread类的几种常见属性 三、线程状态 一、通过jconsole.exe查看线程状态的方法 通过jconsole查看线程状态非常实用的方式 只要你安装了jdk,大致按照这个目录就可以找到这个可执行程序: 然后双击这…...
LeetCode Hot100 零钱兑换
给你一个整数数组 coins ,表示不同面额的硬币;以及一个整数 amount ,表示总金额。 计算并返回可以凑成总金额所需的 最少的硬币个数 。如果没有任何一种硬币组合能组成总金额,返回 -1 。 你可以认为每种硬币的数量是无限的。 示…...
微信小程序接口实现语音转文字
一、效果展示 我们有一个按钮,点击“开始录音”按钮,此时按钮变成“停止录音”并开始计时,点击停止录音后,界面上即可展示返回的文字 二、代码实现 完整代码实现见github 1.小程序端代码 // index.js const recorderManager…...
[Spark Streaming] 读取 Kafka 消息, 插入到 MySQL
以下是一个简单的使用 Spark Streaming 读取 Kafka 消息、统计数据后插入到 MySQL 中的 Scala 代码示例: import org.apache.spark.SparkConf import org.apache.spark.streaming.{Seconds, StreamingContext} import org.apache.spark.streaming.kafka.KafkaUtils…...
精选3款国内wordpress 主题,建站首选
WordPress作为一款功能强大且易于使用的建站平台,已经成为了许多企业和个人搭建网站的首选。为了帮助大家更好地选择适合自己的WordPress主题,小编将为大家推荐三款国内优秀的WordPress主题:子比主题、OneNav主题和RiTheme主题。 1.子比主题…...
JavaScript之 Uint8Array 类型数组(solana pda场景中的大小端)
文章目录 JavaScript之 Uint8Array 类型数组numberToUint8Array 数字转换为Uint8Array为什么要把数字转换为Uint8Array数字转换为Uint8Array的大小端问题solana pda场景中的大小端JavaScript之 Uint8Array 类型数组 Uint8Array 数组类型表示一个8位无符号整型数组,创建时内容…...
《Windows API每日一练》24.1 WinSock简介
本节将逐一介绍WinSock的主要特性和组件,套接字、WinSock动态库的使用。 本节必须掌握的知识点: Windows Socket接口简介 Windows Socket接口的使用 第178练:网络时间校验 24.1.1 Windows Socket接口简介 ■以下是WinSock的主要特性和组件…...
openwrt编译Dockerfile
一、Dockerfile FROM ubuntu:20.04ENV TZAsia/ShanghaiRUN apt-get update && \apt-get install -y --no-install-recommends tzdata && \ln -fs /usr/share/zoneinfo/$TZ /etc/localtime && \dpkg-reconfigure --frontend noninteractive tzdata &am…...
【C语言】分支与循环(循环篇)——结尾猜数字游戏实现
前言 C语言是一种结构化的计算机语言,这里指的通常是顺序结构、选择结构、循环结构,掌握这三种结构之后我们就可以解决大多数问题。 分支结构可以使用if、switch来实现,而循环可以使用for、while、do while来实现。 1. while循环 C语言中…...
【数据结构】链表篇
文章目录 1.链表的概念以及结构2.链表的分类2.1 单向或者双向2.2 带头或者不带头2.3 循环或者不循环2.4 无头单向非循环链表和带头双向循环链表 3.单链表的实现3.1 准备工作3.2 节点的创建3.3 单链表的释放3.4 打印链表3.5 单链表的尾插3.6 单链表的尾删3.7 单链表头删3.8 单链…...
Python SciPy介绍
在数据科学和工程领域,Python已经成为了一个不可或缺的工具,这主要得益于其强大的库和框架支持。其中,SciPy库作为Python科学计算的核心库之一,为研究人员、工程师和数据分析师提供了大量高效的算法和数学工具。本文将带您深入了解…...
docker镜像源
1、直接在服务器上创建这个文件,将镜像源配置在里面 /etc/docker/daemon.json {"registry-mirrors": ["https://do.nark.eu.org","https://dc.j8.work","https://docker.m.daocloud.io","https://dockerproxy.com&qu…...
【clion】clion打开文件目录卡死问题
巨卡,几乎无法打开,据说是fsnotifier64.exe 被限制了。删除 火绒就好了。 关闭windows defender 官方:关闭 Windows 安全中心中的Defender 防病毒保护 此时,删除火绒: 界面变这样了:...
[CR]厚云填补_GridFormer
GridFormer: Residual Dense Transformer with Grid Structure for Image Restoration in Adverse Weather Conditions Abstract 恶劣天气条件下的图像恢复是计算机视觉中的一个难点。在本文中,我们提出了一种新的基于变压器的框架GridFormer,它可以作为…...
观成科技:隐蔽隧道工具Ligolo-ng加密流量分析
1.工具介绍 Ligolo-ng是一款由go编写的高效隧道工具,该工具基于TUN接口实现其功能,利用反向TCP/TLS连接建立一条隐蔽的通信信道,支持使用Let’s Encrypt自动生成证书。Ligolo-ng的通信隐蔽性体现在其支持多种连接方式,适应复杂网…...
零门槛NAS搭建:WinNAS如何让普通电脑秒变私有云?
一、核心优势:专为Windows用户设计的极简NAS WinNAS由深圳耘想存储科技开发,是一款收费低廉但功能全面的Windows NAS工具,主打“无学习成本部署” 。与其他NAS软件相比,其优势在于: 无需硬件改造:将任意W…...
C++初阶-list的底层
目录 1.std::list实现的所有代码 2.list的简单介绍 2.1实现list的类 2.2_list_iterator的实现 2.2.1_list_iterator实现的原因和好处 2.2.2_list_iterator实现 2.3_list_node的实现 2.3.1. 避免递归的模板依赖 2.3.2. 内存布局一致性 2.3.3. 类型安全的替代方案 2.3.…...
Psychopy音频的使用
Psychopy音频的使用 本文主要解决以下问题: 指定音频引擎与设备;播放音频文件 本文所使用的环境: Python3.10 numpy2.2.6 psychopy2025.1.1 psychtoolbox3.0.19.14 一、音频配置 Psychopy文档链接为Sound - for audio playback — Psy…...
3-11单元格区域边界定位(End属性)学习笔记
返回一个Range 对象,只读。该对象代表包含源区域的区域上端下端左端右端的最后一个单元格。等同于按键 End 向上键(End(xlUp))、End向下键(End(xlDown))、End向左键(End(xlToLeft)End向右键(End(xlToRight)) 注意:它移动的位置必须是相连的有内容的单元格…...
LCTF液晶可调谐滤波器在多光谱相机捕捉无人机目标检测中的作用
中达瑞和自2005年成立以来,一直在光谱成像领域深度钻研和发展,始终致力于研发高性能、高可靠性的光谱成像相机,为科研院校提供更优的产品和服务。在《低空背景下无人机目标的光谱特征研究及目标检测应用》这篇论文中提到中达瑞和 LCTF 作为多…...
Java 与 MySQL 性能优化:MySQL 慢 SQL 诊断与分析方法详解
文章目录 一、开启慢查询日志,定位耗时SQL1.1 查看慢查询日志是否开启1.2 临时开启慢查询日志1.3 永久开启慢查询日志1.4 分析慢查询日志 二、使用EXPLAIN分析SQL执行计划2.1 EXPLAIN的基本使用2.2 EXPLAIN分析案例2.3 根据EXPLAIN结果优化SQL 三、使用SHOW PROFILE…...
医疗AI模型可解释性编程研究:基于SHAP、LIME与Anchor
1 医疗树模型与可解释人工智能基础 医疗领域的人工智能应用正迅速从理论研究转向临床实践,在这一过程中,模型可解释性已成为确保AI系统被医疗专业人员接受和信任的关键因素。基于树模型的集成算法(如RandomForest、XGBoost、LightGBM)因其卓越的预测性能和相对良好的解释性…...
游戏开发中常见的战斗数值英文缩写对照表
游戏开发中常见的战斗数值英文缩写对照表 基础属性(Basic Attributes) 缩写英文全称中文释义常见使用场景HPHit Points / Health Points生命值角色生存状态MPMana Points / Magic Points魔法值技能释放资源SPStamina Points体力值动作消耗资源APAction…...
Tauri2学习笔记
教程地址:https://www.bilibili.com/video/BV1Ca411N7mF?spm_id_from333.788.player.switch&vd_source707ec8983cc32e6e065d5496a7f79ee6 官方指引:https://tauri.app/zh-cn/start/ 目前Tauri2的教程视频不多,我按照Tauri1的教程来学习&…...
