Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How does UIViewController manage to have a default no-args initializer in Swift?

Tags:

ios

swift

UIViewController class defines a single designated initializer, init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: NSBundle?) and no convenience initializers, however, it is possible to write the following line of code and have it compile (Xcode 6.1.1)

let vc = UIViewController()

How is this possible?

According to the Swift book, here are the rules of initializer inheritance

Rule 1 If your subclass doesn’t define any designated initializers, it automatically inherits all of its superclass designated initializers.

Rule 2 If your subclass provides an implementation of all of its superclass designated initializers—either by inheriting them as per rule 1, or by providing a custom implementation as part of its definition—then it automatically inherits all of the superclass convenience initializers.”

Excerpt From: Apple Inc. “The Swift Programming Language.” iBooks. https://itun.es/us/jEUH0.l

Therefore UIViewController couldn't have inherited the init() method from its ancestor superclass NSObject, then where does the initializer come from?

On a different note, because every swift class must ultimately call its designated initializer, doesn't that mean initWithCoder: will also end up calling init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: NSBundle?)? However that doesn't seem to be the case in practice.

like image 457
Tony Avatar asked Mar 17 '23 03:03

Tony


2 Answers

The rules in the Swift book apply to classes defined in Swift. UIViewController is a class imported from ObjC, so its behavior is subject to ObjC rules (or lack thereof).

In ObjC, the designated initializer chaining pattern is something you should do (unlike in Swift where the compiler enforces that it's the only thing you can do). If you call an inherited initializer instead of the designated initializer, you'll get back an instance... but there are no guarantees that said instance will be in a sensible state. (In fact, you're pretty much guaranteed it won't be.) Authors of ObjC classes sometimes practice defensive coding by implementing the initializers you're not supposed to call and having them throw exceptions, but that doesn't seem to be the case here.

This particular case is arguably a bug of some sort — it wouldn't be a bad idea to file one with Apple. Either Swift should enforce initializer rules when creating objects from imported ObjC API (in which case UIViewController would need to declare its designated initializers in ObjC with NS_DESIGNATED_INITIALIZER), or UIViewController should be implemented to guard against improper initialization. (Though it's possible there could be compatibility side effects to "fixing" either of those.)

like image 192
rickster Avatar answered Mar 19 '23 15:03

rickster


The comment block above that init function contains the following text:

/*
  The designated initializer. If you subclass UIViewController, you must call the super implementation of this
  method, even if you aren't using a NIB.  (As a convenience, the default init method will do this for you,
  and specify nil for both of this methods arguments.) ...
*/

It is my understanding that because this is an Objective-C class, it is allowed to inherit methods implicitly. If you create a subclass of this class in swift, you will not have access to super.init(). If you create your subclass in Objective-C code without implementing your own -[ init], you will be able to construct your subclass with ().

Because your project drops into Objective-C land with the class that you're calling, it is able to find the selector. A swift subclass of an Objective-C class would follow the swift inheritance rules only.

like image 25
Ian MacDonald Avatar answered Mar 19 '23 17:03

Ian MacDonald