Hello and welcome to iOS performance optimization

This article will explain crash monitoring and anti-crash handling.

  • How do I collect Crash? How do I collect and monitor Crash using third parties, such as Bugly and Umen

  • Anti-crash processing Common crash type anti-crash processing hook scheme security interface

How to collect Crashes

In the common development process, due to the code is not rigorous, such as do not check the input parameters, the use of C++ wild pointer and so on cause program crash. Crash should be regarded as the most serious bug, especially online crash. If the number of App users is large, it may cause a great impact. Therefore, a mechanism is needed to collect crash in projects and solve it in time.

Third parties such as Bugly and Friends are used to collect

Most companies use third-party platforms to collect crash. Bugly, Ameng and TalkingData are more commonly used in the industry. The author recommends Bugly, developed by Tencent, which is lightweight and convenient for monitoring crash and lag.

IOS Development Communication Technology group: 563513413, whether you are big ox or small white welcome to join

Monitoring Crash Mechanism

Most of the top manufacturers will develop crash capture framework by themselves. At this point, it is necessary to understand the crash capture principle. You can read the open source library Kscrash or PlCrashReporter. In fact, the crash capture principle is very simple. There are two main situations to deal with:

1. The OC class is abnormal. The NSException exception was caused by the OC code. We could start by NSGetUncaughtExceptionHandler saved previously registered exception handler, then through NSSetUncaughtExceptionHandler set up our own exception handler, we don’t monitor, needs to be set back to the principle of the exception handler, In our own uncaughtHandleException handler, we need to manually call the original handler.

static NSUncaughtExceptionHandler* g_previousUncaughtExceptionHandler; void installUncaughtExceptionHandler(void){ g_previousUncaughtExceptionHandler = NSGetUncaughtExceptionHandler(); NSSetUncaughtExceptionHandler(&uncaughtHandleException); }void uninstallUncaughtExceptionHandler(void){ if(g_previousUncaughtExceptionHandler){ NSSetUncaughtExceptionHandler(g_previousUncaughtExceptionHandler); }}void uncaughtHandleException(NSException *exception){NSArray *stackArray = [exception callStackSymbols]; NSString *reason = [exception reason]; NSString *name = [exception name]; NSString *exceptionInfo = [NSString stringWithFormat:@"Exception Reason: %@\nException Name: %@\nException Stack: %@",name, reason, stackArray]; NSLog(exceptionInfo); if (g_previousUncaughtExceptionHandler ! = NULL) { g_previousUncaughtExceptionHandler(exception); }}Copy the code

2. Signal capture. Signal is an exception thrown by the underlying iOS Mach Signal exception converted to Signal. Since this is a POSIX-compliant exception, we can also register the corresponding signal with the sigAction function.

static struct sigaction* g_previousSignalHandlers = NULL; Static int g_fatalSignals[] = {SIGHUP, SIGINT, SIGQUIT, SIGABRT, SIGILL, SIGSEGV, SIGFPE, SIGBUS, SIGPIPE}; static int g_fatalSignalsCount = (sizeof(g_fatalSignals) / sizeof(g_fatalSignals[0])); const int* kssignal_fatalSignals(void){ return g_fatalSignals; }int kssignal_numFatalSignals(void){ return g_fatalSignalsCount; }void signalExceptionHandler(int signo, siginfo_t *info, void *uapVoid){ void *frames[128]; int i, len = backtrace(frames, 128); Char **symbols = backtrace_symbols(frames, len); NSMutableString *exceptionContent = [[NSMutableString alloc] init]; [exceptionContent appendFormat:@"signal name: %d \n signal stack: \n",signo]; for (i = 0; i < len; ++i) { [exceptionContent appendFormat:@"%s\r\n", symbols[i]]; } // Free (symbols); raise(signo); }void installSignalHandler(void){ const int* fatalSignals = kssignal_fatalSignals(); int fatalSignalsCount = kssignal_numFatalSignals(); if(g_previousSignalHandlers == NULL){ g_previousSignalHandlers = (struct sigaction *)malloc(sizeof(*g_previousSignalHandlers) * (unsigned)fatalSignalsCount); } struct sigAction action = {{0}}; action.sa_flags = SA_SIGINFO | SA_ONSTACK; sigemptyset(&action.sa_mask); action.sa_sigaction = &signalExceptionHandler; for(int i = 0; i < fatalSignalsCount; i++) { if(sigaction(fatalSignals[i], &action, &g_previousSignalHandlers[i]) ! = 0) {// cancel the listening handler for(I --; i >= 0; i--) { sigaction(fatalSignals[i], &g_previousSignalHandlers[i], NULL); } break; } }}void uninstallSignalHandler(void){ const int* fatalSignals = kssignal_fatalSignals(); int fatalSignalsCount = kssignal_numFatalSignals(); for(int i = 0; i < fatalSignalsCount; i++) { sigaction(fatalSignals[i], &g_previousSignalHandlers[i], NULL); }}Copy the code

Crash prevention

Common Crash Types

According to the author’s experience, most of the crashes in OC are due to the failure to call the method to determine the parameters, such as array added object is not empty, access array elements out of bounds, etc. There are also C++ crashes, such as using wild Pointers.

Anti-crash solution

Since most crashes in OC are caused by not judging the incoming arguments, calling methods to judge the incoming arguments can resolve the crash. There are two ways to solve this kind of crash uniformly: hook scheme and secure interface

Hook plan

This scheme hook the methods of common classes in the system and judge the input parameters. So let’s say that hook NSMutableArray’s addObject method is nullable.

@implementation NSMutableArray (akSafe)+ (void)load { [self swizzMethodOriginalSelector:@selector(addObject:) swizzledSelector:@selector(akSafe_addObject:)]; }+ (void)swizzMethodOriginalSelector:(SEL)originalSelector swizzledSelector:(SEL)swizzledSelector { Method originalMethod = class_getInstanceMethod(self.class, originalSelector); Method swizzledMethod = class_getInstanceMethod(self.class, swizzledSelector); BOOL didAddMethod = class_addMethod(self.class, originalSelector, method_getImplementation(swizzledMethod), method_getTypeEncoding(swizzledMethod)); if (didAddMethod) { class_replaceMethod(self.class, swizzledSelector, method_getImplementation(originalMethod), method_getTypeEncoding(originalMethod)); } else { method_exchangeImplementations(originalMethod, swizzledMethod); }}- (void)aksafe_AddObject:(id)anObject { if (anObject) { [self aksafe_AddObject:anObject]; }}@endCopy the code

Security interface

This scheme encapsulates the methods of common classes in the system and determines the input parameters. You call the safe interface, you call the addObject method that encapsulates NSMutableArray, aksafe_AddObject, and you call aksafe_AddObject to add objects.

@implementation NSMutableArray (aksafe)- (void)aksafe_AddObject:(id)anObject { if (anObject) { [self addObject:anObject]; }}@endCopy the code

Both schemes have advantages and disadvantages. The advantage of the hook scheme is that the business side can call the system method directly, while the disadvantage is that the performance will be damaged due to hook. The security interface scheme is that the business side must call the security interface uniformly, and the advantage is light weight. The author recommends the second scheme, which is lightweight and can be used as a coding specification.