Apteligent crash monitoring for mobile apps allows developers to better understand the root causes of fatal exceptions and errors. Thousands of developers rely on Apteligent for real-time actionable performance data, giving us a broad perspective on some of the issues that plague apps and frustrate developers. We recently analyzed the five most frequent crashes on iOS and found that the vast majority (over 80%) of all crashes on iOS can be categorized into five types:

1. SIGSEGV

SIGSEGV is a signal (SIG) sent to interrupt the code when a segmentation violation (SEGV) occurs. This is the most common crash on iOS, constituting nearly 50% of all crashes — partially due to its generic nature. This signal occurs when your app attempts to access memory that has not been allocated by the program (or memory that has recently been freed).

To debug a SIGSEGV crash, look for variables that have been deallocated then accessed from somewhere else. If the crash is consistent and accessible from your development environment, you can use Zombies to detect objects that are referenced after they have been deallocated.

This crash can also occur during low-memory situations on a user’s device as objects are freed to make room for system resources, making it difficult to replicate and track down. Utilize the stack trace to step through any classes or methods that may be referencing the deallocated object.

A common cause is a dangling delegate or listener that has been deallocated.

// myView can, and should be, a weak reference
self.delegate = myView;

// myView gets popped from the UINavigationController and is deallocated but
// self.delegate is still set - and now points nowhere

[self.delegate doSomething]; // will throw a SIGSEGV error

To resolve this, simply check that the delegate is not nil and can respond to the given selector:

if(self.delegate != nil) {
    if([self.delegate respondsToSelector:@selector(doSomething)]) {
        [self.delegate doSomething];
    }
}

2. NSInvalidArgumentException

The first few lines of the stack trace can usually point you in the right direction to track down this exception. One of the most common causes is shown in the stack trace as an unrecognized selector. This crash occurs when a method is being called on an object that can’t respond to it. For example:

// Since the object doesn’t respond to the method provided, this
// will throw an unrecognized selector NSInvalidArgumentException
[@”myString” objectForKey:@”someKey”];

This obvious coding error won’t make it past the compiler, so keep an eye out for dynamic assignments like JSON deserialization:

// data => [‘item0’, ‘item1’, ‘item2’]
NSDictionary *json = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil];
// json data is an array, not a dictionary

// will throw an NSInvalidArgumentException because it is passing
// an NSArray when an NSDictionary is expected
[json objectForKey:@”myKey”];

3. SIGABRT

This signal (SIG) is an abort (ABRT) message that stops the program. You’ll see this in your debugger when there is an unhandled exception (see #2). However, in a deployed app SIGABRT appears when there is an assertion failure or abort method called within the app or the operating system.

If your stack trace is cryptic or obfuscated, the abort method was likely called within Apple’s code. At that point, the operating system will kill the app and throw out the stack trace, making it difficult to debug. This doesn’t mean the OS code has a bug (sorry, the crash is probably still your fault!). The code simply reached an invalid or unexpected state and couldn’t continue.

SIGABRT codes are often raised during asynchronous system method calls, so keep an eye out for CoreData, accessing files, NSUserDefaults, and other multithreaded system functions.

4. SIGBUS

This signal indicates a bus error and is commonly mixed up with SIGSEGV. While similar (both represent an attempt to access invalid memory), SIGBUS occurs when the address doesn’t exist or more commonly has an invalid alignment. In other words, the physical address is invalid (which is different from SIGSEGV where the logical address is invalid).

This signal can be sent from most synchronous methods and often can be found when the code attempts to access a lock.

5. NSRangeException

A descriptive stack trace helps track down this exception, which occurs when the code tries to access an index outside of the object’s range. Read through the stack trace for a note that looks like:

[__NSArrayM objectAtIndex:]: index 11 beyond bounds [0 .. 10]

The most common objects that cause this are arrays and strings. For example:

NSArray *arr = @[@1, @2, @3, @4];
NSNumber *num = [arr objectAtIndex:9]; // throws NSRange exception

NSString *mainString = @”myString”;
NSString *subString = [mainString substringToIndex:24]; // throws NSRange exception

Avoid these exceptions by ensuring the index being queried fits within the object’s range:

NSNumber *obj = nil;
NSArray *arr = @[@1, @2, @3, @4];

if([arr count] > 9) {
    obj = [arr objectAtIndex:9];
}

NSString *subString = nil;
NSString *mainString = @”myString”;

if([mainString length] > 24) {
    subString = [mainString substringToIndex:24];
}

This is not an exhaustive list of why iOS apps crash. Just because your app works during development and testing doesn’t mean it will work for all of your users on all their different devices, operating systems, geographies, and network conditions. Apteligent Crash Reporting gives you full insight into all the performance issues that stand between your app and its intended user experience. It takes just one line of code to get started on the Apteligent platform and start collecting performance data for your app. Check out the additional capabilities of our solution here.