awakeFromNib method called multiple times

2019-01-15 07:49发布

问题:

In my NSPersistenDocument based project i have a structure like this

myDocument (NSPersistentDocument) -> myDocument.xib (windows xib)
                                           |
                                           |-> view (the self.view) --> ... "other view"
                                           |
                                           |-> some NSArrayController 
                                           |
                                           |-> myResourceViewController --> myResourceViewController.xib
                                                                                          |
                                                                                          |-> view (the self.view)
                                                                                          |
                                                                                          |-> myTreeController (a NSTreeController subclass)

basically, myResourceViewController is an instance of a viewController who manage resourceView and manage their data.

in awakeFromNib method of myDocument i have the following code

- (void)windowControllerDidLoadNib:(NSWindowController *)aController
{
    ...
    [leftBar addSubview:resourceViewController.view]; //i add resourceViewController's view 
    resourceViewController.view.frame = leftBar.bounds;
    ...
}

in myResourceViewController awakeFromNib methods i have:

-(void)awakeFromNib;
{
    NSLog(@"%@", [self description]);

    [removeButton bind:@"enabled" toObject:resourceTreeController withKeyPath:@"selection" options:[NSDictionary dictionaryWithObject:NSIsNotNilTransformerName forKey:NSValueTransformerNameBindingOption]];

    NSArray *draggedTypes = [NSArray arrayWithObjects:ResourceURIPasteBoardType, nil];
    [resourceOutlineView registerForDraggedTypes:draggedTypes];
}

the NSLog say that awakeFromNib, of the same instance of myResourceViewController, is called 4 time, i don't understand why. My only ResourceViewController is created in myDocument xib. I don't use NSNib loading methods everywhere.

回答1:

I found the solution. awakeFromNib is called every time a NSTableCellView is created by NSOutlineView.



回答2:

The root cause is described in the NSTableView header file of the method makeViewWithIdentifier: " .... Note that 'owner' will get an 'awakeFromNib:' call each time the object is instantiated."

My solution is simple but I expect not suitable for all: Just define e.g. the tabelView as owner:

- (NSView *)tableView:(NSTableView *)tableView viewForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row
{
    NSTableCellView *view = [tableView makeViewWithIdentifier:kTextViewIdentifier owner:tableView];

    return view;
}


回答3:

I don't know why it's called four times, but at least I can account for two calls of awakeFromNib. It's important to remember that awakeFromNib is called even for the File's Owner of the nib file, not only the objects contained in the nib file.

Therefore, your ResourceViewController's awakeFromNib gets called at least twice: when it is loaded in myDocument.xib, and then when the view managed by the ResourceViewController is loaded from another nib.

It's better to perform the initialization in other methods which is called on a more definite timing, such as ...didLoad or applicationDidFinish....



回答4:

I put the code within a synchronized block inside my awakeFromNib like so.

e.g

@implementation {
    BOOL _initialize;
}

    - (id)init {
        self = [super init];
        if (self) {
            _initialize = YES;
        }
        return self;
    }

    - (void)awakeFromNib {
        @synchronized(self) {
            if (_initialize) {
                _initialize = NO;

                /* code to execute once */
            }
        }

        /* code to re-execute */
    }
}


回答5:

I noticed the same in NSTableView. NSTableView was updated through NSArrayController and I noticed that NSTableView had delegate set to File Owner, When I removed delegate to File Owner, awakeFromNib called only once.



回答6:

This wasn't easy to figure out but for me somehow I my table view changed to 'View Based' vs 'Cell Based' for the Content Mode.

Switching back to 'Cell Based' and the awakeFromNib only ran once.

Note: select your table view in Interface Builder three times to get to the right level. Or, just select your 'Table View' from the Documents Outline.



回答7:

The solution is not to set the owner to self in makeViewWithIdentifier:owner:

This makes the awakefromNib to be called multiple times.