UITableViewController subclass designated initializer calls [super initWithStyle:] and asserts on all UITableViewController designated initializers. Crash

Originator:henriqueponde90
Number:rdar://23709930 Date Originated:12/1/2015
Status:Closed Resolved:No
Product:iOS Product Version:8.4
Classification:Crash Reproducible:Always
 
Link to Project:https://www.dropbox.com/s/rd24tv30wfv9con/TestTableViewControllerDesignatedInitializers.zip?dl=0
Link to Crash: https://www.dropbox.com/s/fk9tvjt388afa3m/TestTableViewControllerDesignatedInitializers.crash?dl=0

Summary:
I have a subclass of UITableViewController, let's call it TestTableViewController and I only want one designated initializer for it, let's say initWithTitle:

The implementation of initWithTitle: calls [super initWithStyle:] following the guidelines on how designated initializers should call a super designated initializer.

Since I don't want other initializers to be called, I decided to make the 3 designated initializers of the super class unavailable. These are namely:
- (instancetype)initWithStyle:(UITableViewStyle)style NS_DESIGNATED_INITIALIZER;
- (instancetype)initWithNibName:(nullable NSString *)nibNameOrNil bundle:(nullable NSBundle *)nibBundleOrNil NS_DESIGNATED_INITIALIZER;
- (nullable instancetype)initWithCoder:(NSCoder *)aDecoder NS_DESIGNATED_INITIALIZER;

So in TestTableViewController.h I add the NS_UNAVAILABLE to them and in TestTableViewController.m I make them crash like so:

- (instancetype)initWithStyle:(UITableViewStyle)style NS_UNAVAILABLE;
// FIXME:(hponde)
- (instancetype)initWithNibName:(nullable NSString *)nibNameOrNil
                         bundle:(nullable NSBundle *)nibBundleOrNil NS_UNAVAILABLE;
- (nullable instancetype)initWithCoder:(NSCoder *)aDecoder NS_UNAVAILABLE;

and

- (instancetype)initWithStyle:(UITableViewStyle)style NS_UNAVAILABLE
{
    [self doesNotRecognizeSelector:_cmd];
    return nil;
}
- (instancetype)initWithNibName:(nullable NSString *)nibNameOrNil
                         bundle:(nullable NSBundle *)nibBundleOrNil NS_UNAVAILABLE
{
    [self doesNotRecognizeSelector:_cmd];
    return nil;
}
- (nullable instancetype)initWithCoder:(NSCoder *)aDecoder NS_UNAVAILABLE
{
    [self doesNotRecognizeSelector:_cmd];
    return nil;
}

This causes a crash because of the following chain of calls:
[TestTableViewController initWithTitle:]
[UITableViewController initWithStyle:]
[TestTableViewController initWithNibName:bundle:]

So it seems that [UITableViewController initWithStyle:], a designated initializer, is calling [self initWithNibName:bundle:], another designated initializer. This is a violation of the initialization contract, since designated initializer cannot call another initializer in the same class.

Steps to Reproduce:
1. Build the project attached and run
2. Crash

Expected Results:
Should not crash, I'm following the initialization contracts but UITableViewController is not. And that behavior is opaque to me.

Actual Results:
Crash with the stack:

2015-12-01 11:34:55.439 TestTableViewControllerDesignatedInitializers[2820:20725394]  [31m-[TestTableViewController initWithNibName:bundle:]: unrecognized selector sent to instance 0x7f9c1153e4e0
 [0m2015-12-01 11:34:55.441 TestTableViewControllerDesignatedInitializers[2820:20725394]  [31m*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[TestTableViewController initWithNibName:bundle:]: unrecognized selector sent to instance 0x7f9c1153e4e0'
*** First throw call stack:
(
	0   CoreFoundation                      0x000000010d6aec65 __exceptionPreprocess + 165
	1   libobjc.A.dylib                     0x000000010d347bb7 objc_exception_throw + 45
	2   CoreFoundation                      0x000000010d6b60ad -[NSObject(NSObject) doesNotRecognizeSelector:] + 205
	3   TestTableViewControllerDesignatedInitializers 0x000000010ce0e84d -[TestTableViewController initWithNibName:bundle:] + 93
	4   UIKit                               0x000000010dd9b831 -[UITableViewController initWithStyle:] + 45
	5   TestTableViewControllerDesignatedInitializers 0x000000010ce0e966 -[TestTableViewController initWithSingleItem:] + 86
	6   TestTableViewControllerDesignatedInitializers 0x000000010ce0e3d3 -[ViewController viewDidLoad] + 115
	7   UIKit                               0x000000010dbd81d0 -[UIViewController loadViewIfRequired] + 738
	8   UIKit                               0x000000010dbd83ce -[UIViewController view] + 27
	9   UIKit                               0x000000010daf3289 -[UIWindow addRootViewControllerViewIfPossible] + 58
	10  UIKit                               0x000000010daf364f -[UIWindow _setHidden:forced:] + 247
	11  UIKit                               0x000000010daffde1 -[UIWindow makeKeyAndVisible] + 42
	12  UIKit                               0x000000010daa3417 -[UIApplication _callInitializationDelegatesForMainScene:transitionContext:] + 2732
	13  UIKit                               0x000000010daa619e -[UIApplication _runWithMainScene:transitionContext:completion:] + 1349
	14  UIKit                               0x000000010daa5095 -[UIApplication workspaceDidEndTransaction:] + 179
	15  FrontBoardServices                  0x00000001102685e5 __31-[FBSSerialQueue performAsync:]_block_invoke_2 + 21
	16  CoreFoundation                      0x000000010d5e241c __CFRUNLOOP_IS_CALLING_OUT_TO_A_BLOCK__ + 12
	17  CoreFoundation                      0x000000010d5d8165 __CFRunLoopDoBlocks + 341
	18  CoreFoundation                      0x000000010d5d7f25 __CFRunLoopRun + 2389
	19  CoreFoundation                      0x000000010d5d7366 CFRunLoopRunSpecific + 470
	20  UIKit                               0x000000010daa4b02 -[UIApplication _run] + 413
	21  UIKit                               0x000000010daa78c0 UIApplicationMain + 1282
	22  TestTableViewControllerDesignatedInitializers 0x000000010ce0e75f main + 111
	23  libdyld.dylib                       0x000000010fc52145 start + 1
	24  ???                                 0x0000000000000001 0x0 + 1
)
libc++abi.dylib: terminating with uncaught exception of type NSException
 [0m 

Version:
iOS 8.4

Notes:
Does not happen on iOS 9. Since I need to keep iOS 8 support, I can't have the checks

Configuration:
Simulator iPhone 6

Attachments:
'TestTableViewControllerDesignatedInitializers.crash' and 'TestTableViewControllerDesignatedInitializers.zip' were successfully uploaded.

Comments


Please note: Reports posted here will not necessarily be seen by Apple. All problems should be submitted at bugreport.apple.com before they are posted here. Please only post information for Radars that you have filed yourself, and please do not include Apple confidential information in your posts. Thank you!