NSViewController (Bad) Initialization

Since 2008 NSViewController are considered an incomplete implementation, most of all if compared to its counterpart NSWindowController (for example NSViewController doesn’t ensure that its view is added into the responder chain).

Some days ago, I encountered another issue about view controllers which completely drove me crazy; in my application I’ve a left sidebar and a right subview based on selection of the first one: each time the user makes a selection, a new subview is loaded by an equivalent NSViewController and presented to the user.

As well as with NSWindowController I put some basic initialization in awakeFromNib method of NSViewController, to be sure that NIB was loaded and all IBOutlet connected. In particular, in a subview, I had something like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
- (void) awakeFromNib
{
    /* NSArrayController sort descriptor setup */
  [self reloadItems];
}

- (void) reloadItems
{
    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0);
    dispatch_queue_t main = dispatch_get_main_queue();

    dispatch_async(queue, ^{
        [_arrayController fetchWithRequest:nil merge:NO error:nil];

        dispatch_async(main, ^{
            [_tableview reloadData];
        });
    });
}

Occasionally I noticed that reloadItems was called twice (or more), even much time later after initialization. After a boring debug session, I realized that in that subview I also had a view-based NSTableview and the view controller was its datasource and delegate.

That’s means that there also is a method like this:

1
2
3
4
5
6
7
8
9
10
- (NSTableRowView *)tableView:(NSTableView *)tableView rowViewForRow:(NSInteger)row
{
    id result = nil;

    if (tableView == _tableview) {
        result = [[DZMyRowView alloc] initWithFrame:NSMakeRect(0, 0, 100, 100)];
    }

    return result;
}

Each time this table needs a new row, ask its delegate for an instance of NSTableRowView and this causes awakeFromNib to be called again!!

For (at least) this reason, it’s always better initialize NSViewController overriding loadView method or by updating your awakeFromNib like this:

1
2
3
4
5
6
7
8
9
10
11
- (void) awakeFromNib
{
    static BOOL alreadyInit = NO;

    if (!alreadyInit) {
      /* NSArrayController sort descriptor setup */
      [self reloadItems];
      
      alreadyInit = YES;
  }
}

Comments