iOS-解决调用未实现方法程序Crash的问题

iOS-解决调用未实现方法程序Crash的问题,第1张

iOS在调用未实现的方法时会造成程序Crash的问题,其实在Crash之前程序会查看改类有没有实现如下两个方法,如果实现了两方法就会先调用下面的两个方法

那么为避免程序Crash,可以实现上面两个方法,然后通过runtime动态为该行枝烂类添加实现,返回YES表示已处理,NO表示搭野为处理,在没有添加实现时,直接返回YES也是会Crash的,具体实现如下:

class_addMethod()方法只能添加实例方法,无法添加档漏类方法详细见官方文档如下:

官方文档

本文分析了一份标准的iOS应用程序的Crash报告,它通常由以下6个部分组成。

1. 报告头(Header)

报告头包含了应用程序以其运行环境的一些基本信息,下面是报告头的一个例子。

复制代码

Incident Identifier: E6EBC860-0222-4B82-BF7A-2B1C26BE1E85

CrashReporter Key: 6196484647b3431a9bc2833c19422539549f3dbe

Hardware Model: iPhone6,1

Process: TheElements [4637]

Path: /private/var/mobile/Containers/Bundle/Application/5A9E4FC2-D03B-4E19-9A91-104A0D0C1D44/TheElements.app/TheElements

Identifier: com.example.apple-samplecode.TheElements

Version: 1.12

Code Type: ARM (Native)

Parent Process: launchd [1]

Date/Time: 2015-04-06 09:14:08.775 -0700

Launch Time: 2015-04-06 09:14:08.597 -0700

OS Version: iOS 8.1.3 (12B466)

Report Version: 105

2. 异常代码(Exception Codes)

异常代码可能包含异常类型(Exception Type)、异常子类型(Exception Subtype)、处理器的详细异常代码(processor-specific Exception Codes)和其它能提供更多Crash信息的字段,最后一个字段列出了触发Crash的线程索引。下面是异常代码的示例。

复制代码

Exception Type: EXC_CRASH (SIGABRT)

Exception Codes: 0x0000000000000000, 0x0000000000000000

Triggered by Thread: 0

常见的异常类型有以下几种。

a. Bad Memory Access [EXC_BAD_ACCESS // SIGSEGV // SIGBUS]

此类型的Excpetion是最常见的Crash,通常由访问了无效的内存导致。

SIGSEGV:访问了无效地址,没有物理内存对应该地址,通常由于重复释放对象导致。

SIGBUS:总线错误,与 SIGSEGV 不同的是,SIGBUS 访问的是有效地址,但总线访问异常,通常是访问了未对齐的数据。

SEGV:代表无效内存地址,行锋比如空指针、未初始化指针、栈溢出等。

b. Abnormal Exit [EXC_CRASH // SIGABRT]

进程异常退出,造成Crash通常是因为未捕获到Objective-C/C++的毕竖异常。

SIGABRT:收到Abort信号退出,通常Foundation库中的容器为了保护状态正常会做一些检测,例如插入nil到数组中等会遇到此类错误。

c. 其它异常类型

有些异常类型没有被命名,以16进制数字表示。

0xbaaaaaad:意味着该Crash log并非一个真正的Crash,它仅仅只是包含了整个系统某一时刻的运行状态,由用户同时按Home键和音量键触发。

0xbad22222:当VoIP程序在后台太过频繁的激活时,系统可能会终止此类程序。

0x8badf00d:程序启动或者恢复时间过长被watch dog终止。

0xc00010ff:程序执行大量耗费CPU和GPU的运算,导致设备过热,触发系统过热保护被系统终止。

0xdead10cc:程序退到后台时还占用系统资源(如通讯录)被系统终止。

0xdeadfa11:程序无响应用户强制退出。当用户长按电源键,直到屏幕出现关机确认画面后再长按Home键,将强制退出应用。我们可以合理认为用户这么做的原因是应用程序没有响应。

3. 应用详情(Application Specific Information)

有些Crash出现时,会产生额外的信息,这些信息能帮助档数晌用户更好地了解应用程序终止时的运行环境。示例如下。

复制代码

Application Specific Information:

MyApp[134] was suspended with locked system files:

/private/var/mobile/Library/AddressBook/AddressBook.sqlitedb

4. 回溯(Backtrace)

这部分列出了发生Crash时线程的调用栈。示例如下。

复制代码

Thread 0 name: Dispatch queue: com.apple.main-thread

Thread 0 Crashed:

0 TheElements 0x0000000100063fdc -[AtomicElementViewController myTransitionDidStop:finished:context:] (AtomicElementViewController.m:201)

1 UIKit 0x000000018ca5c2ec -[UIViewAnimationState sendDelegateAnimationDidStop:finished:] + 184

2 UIKit 0x000000018ca5c1f4 -[UIViewAnimationState animationDidStop:finished:] + 100

3 QuartzCore0x000000018c380f60 CA::Layer::run_animation_callbacks(void*) + 292

4 libdispatch.dylib 0x0000000198fb9368 _dispatch_client_callout + 12

5 libdispatch.dylib 0x0000000198fbd97c _dispatch_main_queue_callback_4CF + 928

6 CoreFoundation0x000000018822dfa0 __CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__ + 8

7 CoreFoundation0x000000018822c048 __CFRunLoopRun + 1488

8 CoreFoundation0x00000001881590a0 CFRunLoopRunSpecific + 392

9 GraphicsServices 0x00000001912fb5a0 GSEventRunModal + 164

10 UIKit 0x000000018ca8aaa0 UIApplicationMain + 1484

11 TheElements 0x000000010005d800 main (main.m:55)

12 libdyld.dylib 0x0000000198fe2a04 start + 0

Thread 1 name: Dispatch queue: com.apple.libdispatch-manager

Thread 1:

0 libsystem_kernel.dylib0x00000001990e0c94 kevent64 + 8

1 libdispatch.dylib 0x0000000198fc897c _dispatch_mgr_invoke + 272

2 libdispatch.dylib 0x0000000198fbb3b0 _dispatch_mgr_thread + 48

...

5. 线程状态(Thread State)

这部分列出了发生Crash的线程的状态,即寄存器和寄存器的值。示例如下。

复制代码

Thread 0 crashed with ARM Thread State (64-bit):

x0: 0x0000000000000000 x1: 0x0000000000000000 x2: 0x0000000000000000 x3: 0x00000001995f8020

x4: 0x0000000000000000 x5: 0x0000000000000001 x6: 0x0000000000000000 x7: 0x0000000000000000

x8: 0x0000000000000000 x9: 0x0000000000000015 x10: 0x0000000199601df0 x11: 0x0000000b0000000f

x12: 0x00000001741e8700 x13: 0x000001a5995f5779 x14: 0x0000000000000000 x15: 0x0000000044000000

x16: 0x00000001989724d8 x17: 0x0000000188176370 x18: 0x0000000000000000 x19: 0x00000001701dda60

x20: 0x0000000000000001 x21: 0x0000000136606e20 x22: 0x00000001000f6238 x23: 0x0000000000000000

x24: 0x000000019cc640a8 x25: 0x0000000000000020 x26: 0x0000000000000000 x27: 0x0000000000000000

x28: 0x000000019cc577c0 fp: 0x000000016fd1a8d0 lr: 0x00000001000effcc

sp: 0x000000016fd1a860 pc: 0x00000001000effdc cpsr: 0x60000000

6. 二进制映像(Binary Images)

这部分列出了当Crash发生时被装载进进程内存空间的依赖库或者模块。示例如下。

复制代码

Binary Images:

0x100058000 - 0x10006bfff TheElements arm64 <77b672e2b9f53b0f95adbc4f68cb80d6>/var/mobile/Containers/Bundle/Application/CB86658C-F349-4C7A-B73B-CE3B4502D5A4/TheElements.app/TheElements

参考资料

a. 《Understanding and Analyzing iOS Application Crash Reports 》,iOS Crash分析官方文档

b. 《iOS Crash文件的解析(一)》,一篇中文博客

Crash原因

Crash原因有共性,归纳起来有:

�6�1 内存管理错误

�6�1 程序逻辑错误

�6�1 SDK错误 (部署版本<编译版本)

�6�1 主线程阻塞

内存管理错误

内存管理是iPhone开发所要掌握的最基本问题,特别是使用引用计数手动管理内存的情况。内存管理错误包括:

�6�1 内存泄漏:未释放不会再使用对象。比如alloc忘记release,malloc忘记free。可用XcodeProduct菜单下的Analyze功能来解决该问题;

�6�1 引用出错:引用已经被释放的对象指针。很多“莫名其妙”的Crash都是由于窗体经历的生命周期所导致的(viewDidUnload、viewDidLoad),在iOSSimulator里模拟内存警告就可以解决该问题;

�6�1 内存警告:App使用的内存超出设备的限制,iOS将强制挂起App,强制挂起iOS是不会记录Crashlog,Flurry也无法记录。内存泄漏、快速/大量的分配内存都可能导致内存警告,这时候应该尽可能的释放不需要的资源。通过Instruments->Allocations里的Heapshot功能能够找出哪些资源未被释放。

WWDC 2012的Session242 - iOS App Performance_ Memory是专门讨论内存管理这个话题。

程序逻辑错误

数组越界、堆栈溢出、并发 *** 作、逻辑错误。扎实的编码基础、严谨细致的工作习惯、清晰的思路可以避免这类错误;

SDK错误

这个错误出现的现象是有的设备运行正常,有的会Crash。原因是未找到框架、类、方法、属性。比如:用iOS5.0 SDK编译并运行在iOS4.0的设备上,5.0的Twitter框架在4.0的设备上找不到。这种问题常出现在用苹果新发布的Xcode编译原有的工程。

未找到框架的解决办法是:部署版本>= 编译版本。iOS框架向后兼容做的很棒,部署版本>编译版本一般不会出现问题。

未找到类、方法、属性的解决办法是:先判断是否存在再使用

if(NSClassFromString(@"MFMailComposeViewController"))

respondsToSelector:

主线程阻塞

主线程阻塞超过10s,iOS将强制挂起App。把长时间的任务放到后台线程去执行,可使用NSThread,NSOperation, dispatch。WWDC2012的Session235 - iOS App Performance_ Responsiveness有详细的介绍。

解决Crash

思路是:定位Crash的程序代码,预测Crash原因,寻找解决方案,测试。

有多种方式可以定位Crash的程序代码:

�6�1 Debug模宽备枯式时,iOSSimulator断点测试定位Crash的堆栈;

�6�1 真机连接iTunes查看Crashlog (Debug模式下);

�6�1 通过Flurry的错误记录查看;

定位之后,就是重新思考程序上下文逻辑,并有理由的预测Crash出现的原因。预测的越多,理解的越深。

寻找解决方案的方法有:

�6�1 浏览苹果官方SDK文档,找出错误原因;

�6�1 Google搜索Crash输出的信息,重点查找行业内技术论坛:cocoachina、stackoverflow、iphonedevsdk等;

�6�1 查看历届WWDC的视频、示例代码;

�6�1 在工程里添加环境变量: NSZombieEnabled、NSDebugEnabled,输出有价值的信息滚拆;

�6�1 如果未找到任何信息,可以寻求苹果官方论坛、业内技术论坛的帮助;

测试

找到解决方案后就需要测试,测试功能输入输出的准确性、程序性能、是否引入新的bug。测试有专业的测试工程师来负责,但开发工程师不能依赖测试工程师来发现问题,尽量独立解决已知存在的问题。

由于Xcode部署工程到真机上比较耗时间,如果可以的话尽可能用iOSSimulator来测试,以减少测试的时间。

建议开发工程师有一个checklist,在产品测试时自己逐一过一下上面常见的问题,这个能慎洞够避免大部分Crash。下图是我们一个产品的FlurryError记录,那120个错误Session是测试Crash时留下的。当然这个记录是没有包括iOS将强制挂起App的情况。


欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/yw/12424264.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2023-05-25
下一篇 2023-05-25

发表评论

登录后才能评论

评论列表(0条)

保存