When an instance of
View controller loading is a case of the pattern commonly known as delayed initialization. And this pattern isn't unique to view controllers: it occurs in several places in Cocoa, mostly related to nib/storyboard-based UI. For example, any custom class (such as a
UIView subclass, or
NSView subclass on macOS) that you put in a nib isn't fully "ready" for you until its
awakeFromNib method is called.
When you load a nib or storyboard (or when the view controller system does so on your behalf), Cocoa (or Cocoa touch) initializes objects from their archived forms in the nib, then goes through each setting the values of any
IBOutlet properties. Because that is a two-step process (and has to be, because archiving can't preserve links to other objects in the way that nibs need), there's an intermediate time where objects exist (that is, they've finished their
init methods and
super.init chains) but aren't fully set up to match the configuration you created in Interface Builder.
There seems to be some confusion here about just what "initialized" means. The requirement of the Swift language, and strong recommendation in ObjC, is that all properties / instance variable have a defined value by the time the initializer chain is finished running. To put it another way, if you get the value of a property or instance variable after initialization, by reading your code you should know what that value will be.
This definition stands in contrast to the behavior of non-ARC Objective-C: if you don't assign to instance variables during initialization, their values are undefined. They could be zero, they could be garbage, they could be discarded memory from something else.
ObjC with ARC partially enforces a solution to this issue in making sure that all variables are initialized to zero/nil even if you don't manually assign to them during initialization. And Swift enforces it further by requiring at compile time that you manually provide values in initialization (either by assigning to properties where they're declared, by assigning to them inside your
init definition, or by declaring them as Optionals to make their default value nil).
So, when you're dealing with something that comes from a nib or storyboard, having a fully initialized instance means that all properties are "initialized" per the language definition — that is, their values are known. It doesn't necessarily mean that their values are "what you want them to be".
In particular, view controllers are intended to be used in delayed-initialization scenarios that last longer than just "during storyboard loading". For example, you can set up a whole interconnected network of view controllers (in a navigation stack, tab views, master/detail split views, etc). Initially, those VC instances are initialized and know some of their non-IBOutlet properties (such as their titles, for labeling inactive tabs in a tab bar). But the expensive UI infrastructure doesn't need to load until the system asks for the VC's view (typically when the user is about to see it).