Understanding and Fixing iPhone App Crashes on iPad Device or Simulator with Objective-C Stack Trace Analysis

Crash Analysis: Understanding the Stack Trace

=====================================================

In this article, we’ll delve into the world of Objective-C stack traces to understand why an iPhone app is crashing on iPad (device or simulator), despite using a universal build. We’ll explore the code, identify potential issues, and provide solutions.

The Problem

The problem arises when running the app on an iPad device or simulator. The app crashes with a message:

*** -[__NSArrayM insertObject:atIndex:]: object cannot be nil

This error occurs in the loadView method of the BannerViewController. To understand why this is happening, let’s examine the stack trace.

The Stack Trace

The stack trace provides valuable information about where the crash occurred. It shows a chain of method calls leading up to the loadView method:

0   CoreFoundation                      0x0204d02e __exceptionPreprocess + 206
1   libobjc.A.dylib                     0x0151ee7e objc_exception_throw + 44
2   CoreFoundation                      0x02000b6a -[__NSArrayM insertObject:atIndex:] + 314
3   CoreFoundation                      0x02000a20 -[__NSArrayM addObject:] + 64
4   UIKit                               0x00545894 -[UIViewController _addChildViewController:performHierarchyCheck:notifyWillMove:] + 344
5   UIKit                               0x00553b8c -[UIViewController(UIContainerViewControllerProtectedMethods) addChildViewController:] + 68
6   iFormularioNew_Free                 0x0003a27c -[BannerViewController loadView] + 348
7   UIKit                               0x00543ff8 -[UIViewController loadViewIfRequired] + 73
8   UIKit                               0x00544232 -[UIViewController view] + 33
9   iFormularioNew_Free                 0x0003aa81 __63-[BannerViewController bannerView:didFailToReceiveAdWithError:]_block_invoke + 49
10  UIKit                               0x004ae067 +[UIView(UIViewAnimationWithBlocks) _setupAnimationWithDuration:delay:view:options:animations:start:completion:] + 506
11  UIKit                               0x004ae276 +[UIView(UIViewAnimationWithBlocks) animateWithDuration:animations:] + 99
12  iFormularioNew_Free                 0x0003a9fb -[BannerViewController bannerView:didFailToReceiveAdWithError:] + 283
13  iAd                                 0x00072d23 -[ADBannerView _sanitizeAndForwardErrorToDelegate:] + 254
14  iAd                                 0x00072991 __28-[ADBannerView setDelegate:]_block_invoke_0 + 92
15  iAd                                 0x00072916 -[ADBannerView setDelegate:] + 266
16  iFormularioNew_Free                 0x0003a0ae -[BannerViewController initWithContentViewController:] + 382
17  iFormularioNew_Free                 0x0000336e -[AppDelegate application:didFinishLaunchingWithOptions:] + 4718
18  UIKit                               0x00460157 -[UIApplication _handleDelegateCallbacksWithOptions:isSuspended:restoreState:] + 266
19  UIKit                               0x00460747 -[UIApplication _callInitializationDelegatesForURL:payload:suspended:] + 1248
20  UIKit                               0x0046194b -[UIApplication _runWithURL:payload:launchOrientation:statusBarStyle:statusBarHidden:] + 805
21  UIKit                               0x00472cb5 -[UIApplication handleEvent:withNewEvent:] + 1022
22  UIKit                               0x00473beb -[UIApplication sendEvent:] + 85
23  UIKit                               0x00465698 _UIApplicationHandleEvent + 9874
24  GraphicsServices                    0x0193adf9 _PurpleEventCallback + 339
25  GraphicsServices                    0x0193aad0 PurpleEventCallback + 46
26  CoreFoundation                      0x01fc2bf5 __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE1_PERFORM_FUNCTION__ + 53
27  CoreFoundation                      0x01fc2962 __CFRunLoopDoSource1 + 146
28  CoreFoundation                      0x01ff3bb6 __CFRunLoopRun + 2118
29  CoreFoundation                      0x01ff2f44 CFRunLoopRunSpecific + 276
30  CoreFoundation                      0x01ff2e1b CFRunLoopRunInMode + 123
31  UIKit                               0x0046117a -[UIApplication _run] + 774
32  UIKit                               0x00462ffc UIApplicationMain + 1211
33  iFormularioNew_Free                 0x00001234 main + 128

The Solution

The problem arises from the fact that the loadView method is being called while still in the middle of the initWithContentViewController: method. This happens because setting the delegate on _bannerView kicks off an ad load and the need for a view.

To fix this issue, we can:

1. Wait until the viewDidLoad method

We can set the delegate on _bannerView in the viewDidLoad method instead of initWithContentViewController:.

- (void)viewDidLoad {
    // ...
    _bannerView.delegate = self;
}

2. Reorder the calls in initWithContentViewController:

Alternatively, we can reorder the calls to initWithContentViewController: to ensure that _contentController is set before setting the delegate on _bannerView.

- (instancetype)initWithContentViewController:(UIViewController *)contentController {
    self = [super init];
    if (self != nil) {
        _contentController = contentController;

        // On iOS 6 ADBannerView introduces a new initializer, use it when available.
        if ([ADBannerView instancesRespondToSelector:@selector(initWithAdType:)]) {
            _bannerView = [[ADBannerView alloc] initWithAdType:ADAdTypeBanner];
        } else {
            _bannerView = [[ADBannerView alloc] init];
        }
        _bannerView.delegate = self;
    }

    return self;
}

3. Move the loadView implementation to viewDidLoad

We can also get rid of the loadView method and put all the code (minus creating the content view) in viewDidLoad.

- (void)viewDidLoad {
    // ...

    if (_bannerView.bannerLoaded) {
        contentFrame.size.height -= bannerFrame.size.height;
        bannerFrame.origin.y = contentFrame.size.height;
    } else {
        bannerFrame.origin.y = contentFrame.size.height;
    }
    _contentController.view.frame = contentFrame;
    _bannerView.frame = bannerFrame;

    [[NSNotificationCenter defaultCenter] postNotificationName:BannerViewActionWillBegin object:self];

    [UIView animateWithDuration:0.25 animations:^{
        [self.view setNeedsLayout];
        [self.view layoutIfNeeded];
    }];

    // ...
}

By following these solutions, we can fix the issue of the iPhone app crashing on iPad (device or simulator).


Last modified on 2024-10-25