IOS崩溃(NSSetUncaughtExceptionHa…

本文介绍了一种自定义异常处理方法,包括使用UncaughtExceptionHandler类捕获并处理应用崩溃时产生的Signal,如SIGABRT、SIGILL、SIGSEGV等,并在崩溃时弹出对话框提示用户,同时提供了获取崩溃现场信息和设备信息的代码。这种方法有助于提高用户体验,减少闪退带来的负面影响。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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

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

Code example
1
2
3
4
5
6
#import
  
@interface UncaughtExceptionHandler : NSObject{
BOOL dismissed;
}
@end
Code example
1
void InstallUncaughtExceptionHandler();

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

Code example
1
2
3
4
5
6
7
8
9
void InstallUncaughtExceptionHandler(){
  
signal(SIGABRT, MySignalHandler);
signal(SIGILL, MySignalHandler);
signal(SIGSEGV, MySignalHandler);
signal(SIGFPE, MySignalHandler);
signal(SIGBUS, MySignalHandler);
signal(SIGPIPE, MySignalHandler);
}

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

Code example
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
#import "UncaughtExceptionHandler.h"
#include #include
  
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

 

Code example
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
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 前,加入一个函数:

Code example
1
2
3
4
- (void)installUncaughtExceptionHandler
{
InstallUncaughtExceptionHandler();
}

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

Code example
1
[self InstallUncaughtExceptionHandler];

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

iOS开发23:通过归档永久存储数据 - 双子座的个人页面 - 开源中国社区

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

  • Unique Post
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值