runloop是OC当中最基本的一个知识点,本文主要从使用角度讲述如何在应用运行中避免因程序异常导致的应用闪退。应用闪退是很多公司的噩梦,应用闪退会导致用户的大量流失,所以控制程序的稳定性是当前很多公司技术团队的首要任务。
且抛开控制bug根源问题,为避免应用在不同环境下可能因一些不可预估的因素死掉,程序异常的处理措施便显得尤为重要了。浅显的说避免崩溃的根本原理是获取到进程的异常,从而避免异常造成的程序crash,采用苹果提供的进程监听程序代码如下:
// 监听方法1
NSSetUncaughtExceptionHandler(&HandleException);
// 监听方法2
signal(SIGABRT, SignalHandler);
signal(SIGILL, SignalHandler);
signal(SIGSEGV, SignalHandler);
signal(SIGFPE, SignalHandler);
signal(SIGBUS, SignalHandler);
signal(SIGPIPE, SignalHandler);
通过监听方法在获取到进程异常时触发相关处理:
void HandleException(NSException *exception) {
// 异常次数
int32_t exceptionCount = OSAtomicIncrement32(&UncaughtExceptionCount);
if (exceptionCount > UncaughtExceptionMaximum) {
return;
}
// 异常信息
NSArray *callStack = [LHLExceptionHelper backtrace];
NSMutableDictionary *userInfo = [NSMutableDictionary dictionaryWithDictionary:[exception userInfo]];
[userInfo setObject:callStack forKey:UncaughtExceptionHandlerAddressesKey];
NSException *exceptionTemp = [NSException exceptionWithName:exception.name reason:exception.reason userInfo:userInfo];
// 我的处理
LHLExceptionHelper *exceptionHandler = [LHLExceptionHelper new];
[exceptionHandler performSelectorOnMainThread:@selector(makeException:)
withObject:exceptionTemp waitUntilDone:YES];
}
记录异常次数以及获取异常信息等,最后通过获取当前的runloop避免进程crash从而保证应用不闪退,当然也可以告知用户选择退出应用避免因此产生的一些应用内的操作隐患,同时清空监听信息,以便之后继续循环。
(void)makeException:(NSException *)exception {
// 一直循环,等待退出命令
CFRunLoopRef runLoop = CFRunLoopGetCurrent();
CFArrayRef allModes = CFRunLoopCopyAllModes(runLoop);
while (!dismissed) {for (NSString *mode in (__bridge NSArray *)allModes) { CFRunLoopRunInMode((CFStringRef)mode, 0.001, false); }
}
// 清空
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);
}