我们在平时开发过程中经常会遇到的异常类型为Objective-C异常和UNIX信号。
1 Objective-C Exception
1.1 NSInvalidArgumentException
Name of an exception that occurs when you pass an invalid argument to a method, such as anilpointer where a non-nilobject is required.
传递非法参数给一个方法时抛出的异常。
常见场景:
NSNutableDictionaryr操作key或value的函数,如setObject:forKey:、removeObjectForKey等等。
NSMutableArray操作value的函数,如addObject:、 insertObject:atIndex:等等。
NSString操作函数,如initWithString:、initWithFormat:、stringWithString:等等。
这类异常一般都会在苹果官方文档中标出,对于不熟悉的接口,查看官方文档,基本上能够避免很多常见的NSInvalidArgumentException异常。
1.2 NSRangeException
Name of an exception that occurs when attempting to access outside the bounds of some data, such as beyond the end of a string.
尝试访问某些数据范围之外时抛出的异常。
常见场景:
NSArray包含索引的操作,如insertObject:atIndex:、objectAtIndex:等等。
NSString包含索引的操作,如characterAtIndex:、getCharacters:range:等等。
对于涉及到范围的接口,需对传入的索引、NSRange范围进行合法性检查,判断是否在集合数据范围内。
1.3 NSFileHandleOperationException
Raised byNSFileHandleif attempts to determine file-handle type fail or if attempts to read from a file or channel fail.
如果尝试确定文件句柄类型失败或尝试读取文件或通道失败,则会抛出此异常。
常见场景:
空间不足:会提示No space left on devie。
没有读写权限:会提示Bad file descriptor。
读文件失败。
在操作文件时,要验证文件句柄的有效性,对文件大小进行校验,对存储空间进行判断。
1.4 KVO引起的异常
常见场景【环境:Xcode12 IOS14】:
没有实现observeValueForKeyPath方法
抛出NSInternalInconsistencyException异常:
在VC pop时没有移除KVO监听
在IOS11及以上的系统不移除也不会崩溃。
多次移除KVO
抛出NSRangeException异常:
监听者和被监听者的生命周期不同
添加或移除时keyPath参数为nil
直接传递出错概率非常低,但传参路径复杂时容易出错
1.5 NSMallocException
iOS Crash之NSMallocException。
2 Mach异常和Unix信号(两者几乎是一一对应的:硬件产生的信号(通过CPU陷阱)被Mach层捕获【Mach异常】,BSD转换为Unix信号)
2.1 SIGSEGV (EXC_BAD_ACCESS)
一般是由于内存地址不合法导致SIGSEGV,例如访问未申请的虚拟内存地址,或者写入没有写的内存。子码可能有以下几种:
KERN_INVALID_FAILURE:试图访问未映射的内存导致的,包括访问数据和取指令。
KERN_PROTECTION_FAILURE:试图使用受保护的有效内存地址导致的,包括只读内存区域或不可执行内存区域。
SEGV_MAPERR:表示堆栈映射错误。
2.2 SIGBUS (EXC_BAD_ACCESS)
一般是由于地址未对齐导致的,例如内存地址对齐出错,或者试图执行没有权限的代码地址。子码有以下几种情况:
KERN_MEMORY_ERROR:试图访问当时无法返回数据的内存,如内存映射文件不可用。
EXC_ARM_DA_ALIGN:试图访问没有正确对齐的内存。此异常代码很少见,因为64位ARM CPU可处理未对齐的数据。但是,如果内存地址既未对齐又位于未映射的内存区域中,则可能会看到此异常子类型。
2.3 EXC_BAD_INSTRUCTION(SIGILL)
非法指令,通常与特定非法或未定义指令或操作数相关。
2.4 EXC_BREAKPOINT(SIGTRAP)
在ARM处理器上,断点异常类型指示跟踪陷阱中断进程。 跟踪陷阱使附上的调试器有机会在执行特定位置时中断该进程。
断点异常类型指示跟踪陷阱中断了该过程。 跟踪陷阱使附加的调试器有机会在执行的特定点中断该进程。 在ARM处理器上,它显示为EXC_BREAKPOINT(SIGTRAP)。 在x86_64处理器上,它显示为EXC_BAD_INSTRUCTION(SIGILL)。
Swift运行时将跟踪陷阱用于特定类型的不可恢复的错误-有关这些错误的信息,请参见Addressing Crashes from Swift Runtime Errors。 一些较低级别的库(例如Dispatch)会在遇到不可恢复的错误时使用此异常来捕获进程,并在崩溃报告的“其他诊断信息”部分中记录有关该错误的其他信息。 有关这些消息的信息,请参阅Diagnostic Messages。
当使用swift时,以下几种情况也会抛出此异常:
一个非可选类型值为nil;
强制类型转换失败;
如果要在自己的代码中使用相同的技术来解决不可恢复的错误,请调用__builtin_trap()函数。 这使系统可以生成带有线程回溯的崩溃报告,以显示你如何达到不可恢复的错误。
ILL_ILLTRP:ILL_ILLTRP at 0xxxxx通常是二进制出错,典型比如app升级前后二进制缓存出错。
2.5 EXC_ARITHMETIC(SIGFPE)
崩溃的线程执行了无效的算术运算。
包括除以0或取余0的情况,及发生数据溢出导致的除以0或取余0的情况;包括浮点错误。
The following values can be placed in si_code for a SIGFPE signal:
FPE_INTDIV Integer divide by zero.
FPE_INTOVF Integer overflow.
FPE_FLTDIV Floating-point divide by zero.
FPE_FLTOVF Floating-point overflow.
FPE_FLTUND Floating-point underflow.
FPE_FLTRES Floating-point inexact result.
FPE_FLTINV Floating-point invalid operation.
FPE_FLTSUB Subscript out of range.
2.6 SIGABRT
通常,发送此信号是因为进程调用了abort函数,例如,当应用遇到未捕获的Objective-C或C ++异常时。
2.7 SIGKILL
此信号表示系统中止进程,通常是调用函数exit()或kill(9)产生。
崩溃报告会包含代表中止原因的编码:
0x8badf00d:ate bad food,系统监视程序由中止无响应应用。注意在生命周期的不同阶段,触发看门狗机制的超时时间是不一样的。
0xc00010ff:cool off,系统由于过热保护中止应用,通常与特定的手机和环境有关。
0xdead10cc:dead lock,系统中止在挂起期间一直保持文件锁或SQLite数据库锁的应用。
0xbaadca11:bad all,系统由于应用在响应PushKit通知时无法报告CallKit呼叫而中止它。
0xbad22222:系统由于VoIP应用恢复太频繁而中止它。
0xc51bad01:watchOS终止了该应用程序,因为它在执行后台任务时占用了过多的CPU时间。
0xc51bad02:watchOS终止了该应用程序,因为它未能在分配的时间内完成后台任务。
0xc51bad03:watchOS终止了该应用程序,因为它未能在分配的时间内完成后台任务,但是系统总体上非常繁忙,以至于该应用程序可能没有收到太多的CPU时间来执行后台任务。
0xbada5e47:系统可能由于你启动了过多了后台任务而中止你的应用。
tips: SIGABRT和SIGKILL区别
两者都是发送给进程的中止信号。
SIGKILL等价于"kill -9",它是用来杀死僵尸进程;而SIGABRT等价于"kill -6",它是用来杀死正在运行的进程。
SIGKILL不能被捕获或忽略,接受进程也不能在收到此信号后做任何清理操作;而SIGABRT可以被捕获,但不能阻塞。
2.8 EXC_GUARD
受保护资源的非法访问,一般是由违背受保护资源防护触发,例如非法访问某些文件描述符。
2.9 EXC_RESOURCE
资源受限,应用由于达到资源的消耗限制而被退出,例如线程调度太频繁等。
3 C++异常
苹果开发同时支持OC和C++语法,其底层也基本上是由C++编写的,所以APP在IOS或OSX中运行时可能会抛C++异常。系统在捕获C++异常后的处理有两种情况:
1、如果此C++异常可以转换为OC异常,则抛给OC异常处理机制;
2、如果此C++异常不能转换为OC异常,则使用_cxa_rethrow()再次抛出;
网友评论