10

It is my understanding that awakeFromNib will always be called before viewDidLoad.

So I have a subclass of a UITableViewController, which is unarchived from a xib file.

I defined these two methods inside:

- (void)awakeFromNib {
  [super awakeFromNib];
  NSLog(@"awake from nib");
}

- (void)viewDidLoad {
  [super viewDidLoad];
  NSLog(@"view did load");
}

what happens is that "view did load" shows up before "awake from nib" in the console. I tried to use a breakpoint at [super awakeFromNib], and repeatedly hit F7 (Step Into), and to my surprise, it stepped into -(void)viewDidLoad BEFORE going to the second line inside awakeFromNib.

Does anyone have a clue what is going on here? I did the exact same thing in a subclass of a regular UIViewController, and the log statements are reversed, as I initially expected...

bogardon
  • 896
  • 2
  • 10
  • 22

4 Answers4

13

To understand that fact I recommend you to see loadNibNamed:owner:options: method of NSBundle.

When you init a view controller from nib, first of all it loads views that are contained there, then it sets file owners properties according to nib. viewDidLoad method is called when it sets file owner's view property to one of the views that were loaded. And awakeFromNib is called when all file owners outlets and properties are set (including view property). So it makes sense that viewDidLoad is called earlier than awakeFromNib.

Hope this'll help

Zapko
  • 2,461
  • 25
  • 30
  • 2
    I'm not sure you're right. Look at this :http://stackoverflow.com/questions/377202/which-should-i-use-awakefromnib-or-viewdidload – Idan Jun 10 '11 at 08:21
  • So where is conflict with what I'm saying? The fact is that when memory warning occurs view controllers view (if it is not visible) is set to nil and deallocated. And when it becomes visible again view controller loads its view from its xib file and sets `self.view = viewLoadedFromXib` so viewDidLoad is called again while it doesn't configure itself again doesn't call awakeFromNib again. – Zapko Jun 10 '11 at 08:38
  • If you don't believe me or I don't write clearly you can check [documentation](http://developer.apple.com/library/ios/#featuredarticles/ViewControllerPGforiPhoneOS/BasicViewControllers/BasicViewControllers.html%23//apple_ref/doc/uid/TP40007457-CH101-SW19) yourself. Anyway it will be more userful. – Zapko Jun 10 '11 at 09:01
  • You focused on the wrong thing, memory warning was not what I intended you to look at. Anyway, see my edited answer. – Idan Jun 10 '11 at 13:28
  • @Zapko I guess at the time of my comment the setting of the view property to nil is no longer performed when the view is not visible. What is the reason for this? I ask this because nowadays subviews of the view are never declared as weak. – Rohan Bhale May 28 '20 at 13:45
  • @RohanBhale unfortunately I don't remember what iOS version it was, but at some point Apple invented some smart way to handle memory warnings without deallocation of the view hierarchy (either archive and unarchive the whole thing as a memory snapshot, or maybe just don't do anything because there is plenty of memory these days : ) But it effectively leads to situation that view and all the subviews are created once per VC lifecycle and there is no need to release them, thus no need having this variables `weak`. – Zapko Jul 09 '20 at 02:26
  • 1
    @Zapko Thank you very much and much appreciated. There is no discussion around this point in any forums. In addition my question for the same got blown over on SO despite being a valid question. – Rohan Bhale Jul 09 '20 at 06:43
6

I create a test project with Navigation-based Application option, and add following codes to rootViewController.m.

- (void)awakeFromNib {
     NSLog(@"awakeFromNib 1");
     [super awakeFromNib];
     NSLog(@"awakeFromNib 2");
}

- (void)viewDidLoad {
     NSLog(@"viewDidLoad 1");
     [super viewDidLoad];
     NSLog(@"viewDidLoad 2");
}

Then, I got the results from console:

awakeFromNib 1
awakeFromNib 2
viewDidLoad 1
viewDidLoad 2

The -(void)viewDidLoad will be called when the view of controller is loaded. So, when the first time you use self.view = ..., the -(void)viewDidLoad will be invoke.


If you wrote something like followings, then -(void)viewDidLoad will be called first.

  - (void)awakeFromNib {
       NSLog(@"awakeFromNib 1");

       // The log sequence will be funny, if `viewDidLoad` raised before [super awakeFromNib]
       // If you are curios about it, just give it a try.           
       // self.view.backgroundColor = [UIColor clearColor];

       [super awakeFromNib];

       /// viewDidLoad will be called 
       /// because self.view must be loaded first.
       self.view.backgroundColor = [UIColor clearColor];  

       NSLog(@"awakeFromNib 2");
  }

and obtain following results.

awakeFromNib 1
viewDidLoad 1
viewDidLoad 2
awakeFromNib 2

Update

loadViewIfNeeded will trigger viewDidLoad if it loads view successfully. Sometimes I will call loadViewIfNeeded to ensure the @IBOutlet instances were initialized, and not null anymore.

AechoLiu
  • 17,522
  • 9
  • 100
  • 118
5

I don't think you have to call awakeFromNib on your super class.

Check this.

Edit

I just ran a quick test, here's the results:

Scenario 1: MainWindow.Xib has a UIViewController subclass TestingAwakeFromNibViewController, Which has it's own Nib file TestingAwakeFromNibViewController.xib.

TestingAwakeFromNibViewController has an UIButton Outlet called btn3. Testing the following code :

- (void)viewDidLoad
{
    [super viewDidLoad];

    NSLog(@"Btn3 %@",btn3);

    NSLog(@"viewDidLoad");
}


-(void) awakeFromNib
{
    [super awakeFromNib];

    NSLog(@"Btn3 %@",btn3);

    NSLog(@"awakeFromNib");
}

Would Print:

Btn3 (null)
AwakeFromNib
Btn3 <UIRoundedRectButton: 0x64088e0; frame = (114 211; 72 37); opaque = NO; autoresize = RM+BM; layer = <CALayer: 0x6408890>>
ViewDidLoad

Scenario 2: Removing the xib file, adding a UIView as a son to the TestingAwakeFromNibViewController inside MainWindow.Xib, and adding UIButton as a subview to the UIView (and connecting the UIbutton outlet to the appropriate outlet of TestingAwakeFromNibViewController).

Now running the above code would print:

Btn3 <UIRoundedRectButton: 0x4e31c30; frame = (114 211; 72 37); opaque = NO; autoresize = RM+BM; layer = <CALayer: 0x4e31be0>>
viewDidLoad
Btn3 <UIRoundedRectButton: 0x4e31c30; frame = (114 211; 72 37); opaque = NO; autoresize = RM+BM; layer = <CALayer: 0x4e31be0>>
awakeFromNib

Meaning ViewDidLoad prior to AwakeFromNib.

Third Scenario: Same as the second, just without calling [super awakeFromNib];

Btn3 <UIRoundedRectButton: 0x4e0ddf0; frame = (114 211; 72 37); opaque = NO; autoresize = RM+BM; layer = <CALayer: 0x4e0dda0>>
awakeFromNib

Now ViewDidLoad is not even getting called.

So, it seems like different scenarios are calling for different action, and we need to prepare ourselves according to the one we're acting upon.

Idan
  • 5,717
  • 10
  • 47
  • 84
  • Interesting. If [super awakeFromNib] is not called then viewDidLoad is not called at all. So all this time, [super awakeFromNib] call viewDidload? – user4951 Jun 06 '12 at 13:14
1

Without being an expert, and following this posts, I realise than in a Tab Controller scenario, In the "child" view controller, the awakeFromNib method is executed when tab controller (parent) is loaded, but viewDidLoad only when its "Tab" is clicked.

And, therefore I understand that this can be used to load data only when a specific tab is selected (clicked)

Albert Català
  • 2,026
  • 2
  • 29
  • 35