如何让IOS应用从容地崩溃

虽然大家都不愿意看到程序崩溃,但可能崩溃是每个应用必须面对的现实,既然崩溃已经发生,无法阻挡了,那我们就让它崩也崩得淡定点吧。

IOS SDK中提供了一个现成的函数 NSSetUncaughtExceptionHandler 用来做异常处理,但功能非常有限,而引起崩溃的大多数原因如:内存访问错误,重复释放等错误就无能为力了,因为这种错误它抛出的是Signal,所以必须要专门做Signal处理。首先定义一个UncaughtExceptionHandler类,.h头文件的代码如下:

#import <UIKit/UIKit.h>

@interface UncaughtExceptionHandler : NSObject

{

BOOL dismissed;

}

@end

void InstallUncaughtExceptionHandler();

 

然后在.mm文件实现InstallUncaughtExceptionHandler(),如下:

void InstallUncaughtExceptionHandler()

{

signal(SIGABRT, MySignalHandler);

signal(SIGILL, MySignalHandler);

signal(SIGSEGV, MySignalHandler);

signal(SIGFPE, MySignalHandler);

signal(SIGBUS, MySignalHandler);

signal(SIGPIPE, MySignalHandler);

}

 

这样,当应用发生错误而产生上述Signal后,就将会进入我们自定义的回调函数MySignalHandler。为了得到崩溃时的现场信息,还可以加入一些获取CallTrace及设备信息的代码,.mm文件的完整代码如下:

#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;

const NSInteger UncaughtExceptionHandlerSkipAddressCount = 4;

const NSInteger UncaughtExceptionHandlerReportAddressCount = 5;

@implementation UncaughtExceptionHandler

+ (NSArray *)backtrace

{

        void* callstack[128];

 int frames = backtrace(callstack, 128);

 char **strs = backtrace_symbols(callstack, frames); 

 int i;

 NSMutableArray *backtrace = [NSMutableArray arrayWithCapacity:frames];

 for (

 i = UncaughtExceptionHandlerSkipAddressCount;

 i < UncaughtExceptionHandlerSkipAddressCount +

UncaughtExceptionHandlerReportAddressCount;

i++)

 {

 [backtrace addObject:[NSString stringWithUTF8String:strs[i]]];

 }

 free(strs); 

 return backtrace;

}

- (void)alertView:(UIAlertView *)anAlertView clickedButtonAtIndex:(NSInteger)anIndex

{

if (anIndex == 0)

{

dismissed = YES;

}

}

- (void)handleException:(NSException *)exception

{

UIAlertView *alert =

[[[UIAlertView alloc]

initWithTitle:NSLocalizedString(@"Unhandled exception", nil)

message:[NSString stringWithFormat:NSLocalizedString(

@"You can try to continue but the application may be unstable.\n"

@"%@\n%@", nil),

[exception reason],

[[exception userInfo] objectForKey:UncaughtExceptionHandlerAddressesKey]]

delegate:self

cancelButtonTitle:NSLocalizedString(@"Quit", nil)

otherButtonTitles:NSLocalizedString(@"Continue", nil), nil]

autorelease];

[alert show];

CFRunLoopRef runLoop = CFRunLoopGetCurrent();

CFArrayRef allModes = CFRunLoopCopyAllModes(runLoop);

while (!dismissed)

{

for (NSString *mode in (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];

}

}

@end

NSString* getAppInfo()

{

    NSString *appInfo = [NSString stringWithFormat:@"App : %@ %@(%@)\nDevice : %@\nOS Version : %@ %@\nUDID : %@\n",

                          [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleDisplayName"],

                          [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleShortVersionString"],

                          [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleVersion"],

                          [UIDevice currentDevice].model,

                          [UIDevice currentDevice].systemName,

                          [UIDevice currentDevice].systemVersion,

                          [UIDevice currentDevice].uniqueIdentifier];

    NSLog(@"Crash!!!! %@", appInfo);

    return appInfo;

}

void MySignalHandler(int signal)

{

int32_t exceptionCount = OSAtomicIncrement32(&UncaughtExceptionCount);

if (exceptionCount > UncaughtExceptionMaximum)

{

return;

}

NSMutableDictionary *userInfo =

[NSMutableDictionary

dictionaryWithObject:[NSNumber numberWithInt:signal]

forKey:UncaughtExceptionHandlerSignalKey];

NSArray *callStack = [UncaughtExceptionHandler backtrace];

[userInfo

setObject:callStack

forKey:UncaughtExceptionHandlerAddressesKey];

[[[[UncaughtExceptionHandler alloc] init] autorelease]

performSelectorOnMainThread:@selector(handleException:)

withObject:

[NSException

exceptionWithName:UncaughtExceptionHandlerSignalExceptionName

reason:

[NSString stringWithFormat:

NSLocalizedString(@"Signal %d was raised.\n"

                                          @"%@", nil),

signal, getAppInfo()]

userInfo:

[NSDictionary

dictionaryWithObject:[NSNumber numberWithInt:signal]

forKey:UncaughtExceptionHandlerSignalKey]]

waitUntilDone:YES];

}

void InstallUncaughtExceptionHandler()

{

signal(SIGABRT, MySignalHandler);

signal(SIGILL, MySignalHandler);

signal(SIGSEGV, MySignalHandler);

signal(SIGFPE, MySignalHandler);

signal(SIGBUS, MySignalHandler);

signal(SIGPIPE, MySignalHandler);

}

 

在应用自身的 didFinishLaunchingWithOptions 前,加入一个函数:

- (void)installUncaughtExceptionHandler

{

InstallUncaughtExceptionHandler();

}

 最后,在 didFinishLaunchingWithOptions 中加入这一句代码就行了:

[self InstallUncaughtExceptionHandler];

现在,基本上所有崩溃都能Hold住了。崩溃时将会显示出如下的对话框:

 

这样在崩溃时还能从容地弹出对话框,比起闪退来,用户也不会觉得那么不爽。然后在下次启动时还可以通过邮件来发送Crash文件到邮箱,这就看各个应用的需求了。

时间: 2024-10-31 14:46:44

如何让IOS应用从容地崩溃的相关文章

iOS的应用程序崩溃率为何高于Android

应用崩溃的主要原因之一就是目前iOS和Android操作系统的分散和混乱.随着苹果和谷歌都发布了更多的全新操作系统,应用开发人员在测试自己开发的应用时需要面临着更多的操作系统.据调查显示,12月1日到15日之间,至少有23个不同iOS系统和33个Android系统存在着应用崩溃的情况. 图:iOS应用的整体崩溃了要高于Android iOS应用的崩溃率要高于Android 应用崩溃的情况同时存在于Android和iOS两大平台,而且其中占比例最大的是iOS 5.0.1,整体崩溃率达到了28.64

为什么iOS的应用程序崩溃率高于Android

编者按:有没有想过为什么某些应用程序会如此频繁的崩溃吗?原因是多种多样的,根据你所使用的设备不同原因也是不同的,比如说苹果iOS设备(iPhone.iPad)或Android设备.本文将通过详实的统计数据为读者详细解析. 应用崩溃的主要原因之一就是目前iOS和Android操作系统的分散和混乱.随着苹果和谷歌都发布了更多的全新操作系统,应用开发人员在测试自己开发的应用时需要面临着更多的操作系统.据调查显示,12月1日到15日之间,至少有23个不同iOS系统和33个Android系统存在着应用崩溃

苹果iOS 8导致应用崩溃频率增加:升级需谨慎

苹果iOS 8导致应用崩溃频率增加:升级需谨慎新浪科技讯 北京时间9月24日上午消息,分析公司Crittercism的最新报告显示,苹果最新一代iOS 8操作系统会导致Facebook和Dropbox等公司的应用崩溃的频率增加.很多用户都已经前往苹果App Store发帖抱怨.一名用户表示,在升级了iOS 8系统后,Facebook应用频繁出现崩溃.文件分享服务Dropbox已经对应用进行了升级,以修复相关问题.Crittercism表示,旧iPhone用户遭遇的问题最多.该公司CEO安德鲁·莱

iOS版GoogleVoice频繁崩溃撤出苹果应用商店

新浪科技讯 北京时间10月17日早间消息,由于最新版软件频频崩溃,谷歌已经从苹果App Store中撤下了iPhone版Google Voice应用. 谷歌对美国科技博客Engadget称,该应用的最新升级导致用户在登录时发生软件崩溃.谷歌表示,该应用将在故障修复后重新上架.已经安装了Google Voice的iPhone用户则可以继续使用这款应用. 美国科技博客Business Insider称,自从升级为iOS 5系统后,Google Voice便频频出现故障,几乎每次查看收件箱时都会崩溃.

iOS中 iOS10 权限崩溃问题 韩俊强的CSDN博客

       今天 手机升级了 iOS10 Beta,然后用正在开发的项目 装了个ipa包,发现点击有关 权限访问 直接Crash了,并在控制台输出了一些信息: This app has crashed because it attempted to access privacy-sensitive data without a usage description.  The app's Info.plist must contain an NSContactsUsageDescription

IOS环信初始化崩溃

问题描述 在启动里调用了那两句环信的代码.结果程序启动完成retun yes 后,就崩溃了.跳转到0x1d4f24 <+284>: b 0x1d4ef0 ; <+232> at DDXMLNode.m:295输出显示pointer being freed was not allocated 解决方案 http://blog.csdn.net/nogodoss/ ... 10349解决方案二: 调用哪俩句代码啊,截图解决方案三: 就是官方文档上的那两句解决方案四: 我也有这问题,怎么

iOS进入后台就崩溃

问题描述 崩溃信息:Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[NSConcreteNotification beginBackgroundTaskWithExpirationHandler:]: unrecognized selector sent to instance 0x7c99a2c0' 登陆注册.收发消息.添加好友等等功能都正常,但是一按Home键进入后台后就崩溃

iOS 刷新聊天界面崩溃

问题描述 Assertion failure in -[UITableViewRowData _assertValidIndexPath:allowEmptySection:], /BuildRoot/Library/Caches/com.apple.xbs/Sources/UIKit_Sim/UIKit-3505.16/UITableViewRowData.m:2310 出现NSScanner: nil string argument 怎没 解决方案 if ([self.dataSource

Android和Ios的crash reporter(崩溃报告采集与上传)

Crash Report,这在大型软件开发领域是很常见的功能,就是能够当程序崩溃退出后,能够将崩溃时的信息,最好是携带dmp文件发送给服务器,这样开发人员既可以获得分发出去的客户端的崩溃率统计,也可以针对出现的错误进行及时的纠正,之前在PC的端游时代,这是很常见的做法,最近进行了在手游上的关于crash report的相关研究,并且为项目编写了一个相对完善的CrashReport模块.        这个模块的来源于手游项目正式上线,但是很多玩家反馈闪退,但是我们只能听到反馈闪退,却不能找到原因