虽然大家都不愿意看到程序崩溃,但可能崩溃是每个应用必须面对的现实,既然崩溃已经发生,无法阻挡了,那我们就让它崩也崩得淡定点吧。
IOS SDK中提供了一个现成的函数 NSSetUncaughtExceptionHa
#import
@interface UncaughtExceptionHandler
{
BOOL dismissed;
}
@end
void InstallUncaughtException
然后在.mm文件实现InstallUncaughtException
void InstallUncaughtException
{
signal(SIGABRT, MySignalHandler);
signal(SIGILL, MySignalHandler);
signal(SIGSEGV, MySignalHandler);
signal(SIGFPE, MySignalHandler);
signal(SIGBUS, MySignalHandler);
signal(SIGPIPE, MySignalHandler);
}
这样,当应用发生错误而产生上述Signal后,就将会进入我们自定义的回调函数MySignalHandler。为了得到崩溃时的现场信息,还可以加入一些获取CallTrace及设备信息的代码,.mm文件的完整代码如下:
#import "UncaughtExceptionHandler
#include
#include
NSString * const UncaughtExceptionHandler
NSString * const UncaughtExceptionHandler
NSString * const UncaughtExceptionHandler
volatile int32_t UncaughtExceptionCount = 0;
const int32_t UncaughtExceptionMaximum
const NSInteger UncaughtExceptionHandler
const NSInteger UncaughtExceptionHandler
@implementation UncaughtExceptionHandler
+ (NSArray *)backtrace
{
UncaughtExceptionHandler
i++)
}
- (void)alertView:(UIAlertView *)anAlertView clickeonAtIndex:(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:UncaughtExceptionHandler
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);
NSSetUncaughtExceptionHa
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:UncaughtExceptionHandler
{
kill(getpid(), [[[exception userInfo] objectForKey:UncaughtExceptionHandler
}
else
{
[exception raise];
}
}
@end
NSString* getAppInfo()
{
}
void MySignalHandler(int signal)
{
int32_t exceptionCount = OSAtomicIncrement32(&UncaughtExceptionCount);
if (exceptionCount > UncaughtExceptionMaximum
{
return;
}
NSMutableDictionary *userInfo =
[NSMutableDictionary
dictionaryWithObject:[NSNumber numberWithInt:signal]
forKey:UncaughtExceptionHandler
NSArray *callStack = [UncaughtExceptionHandler
[userInfo
setObject:callStack
forKey:UncaughtExceptionHandler
[[[[UncaughtExceptionHandler
performSelectorOnMainThr
withObject:
[NSException
exceptionWithName:UncaughtExceptionHandler
reason:
[NSString stringWithFormat:
NSLocalizedString(@"Signal %d was raised.\n"
signal, getAppInfo()]
userInfo:
[NSDictionary
dictionaryWithObject:[NSNumber numberWithInt:signal]
forKey:UncaughtExceptionHandler
waitUntilDone:YES];
}
void InstallUncaughtException
{
signal(SIGABRT, MySignalHandler);
signal(SIGILL, MySignalHandler);
signal(SIGSEGV, MySignalHandler);
signal(SIGFPE, MySignalHandler);
signal(SIGBUS, MySignalHandler);
signal(SIGPIPE, MySignalHandler);
}
在应用自身的 didFinishLaunchingWithOp
- (void)installUncaughtException
{
InstallUncaughtException
}
最后,在 didFinishLaunchingWithOp
[self InstallUncaughtException
现在,基本上所有崩溃都能Hold住了。崩溃时将会显示出如下的对话框:
![如何让iOS从容地崩溃](https://i-blog.csdnimg.cn/blog_migrate/a4c26d1e5885305701be709a3d33442f.gif)
这样在崩溃时还能从容地弹出对话框,比起闪退来,用户也不会觉得那么不爽。然后在下次启动时还可以通过邮件来发送Crash文件到邮箱,这就看各个应用的需求了。