AwakeFromNib called twice for sheet

  • Hi,

    My app (OSX) has a sheet to obtain data from a server and display them in an NSTableView. So far I was using a Cell based table and it worked fine. Yesterday I decided to make the table view based, following the instruction by Apple <https://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/Tab
    leView/PopulatingViewTablesWithBindings/PopulatingView-TablesWithBindings.h
    tml#//apple_ref/doc/uid/10000026i-CH13-SW1
    >  The only difference with that code is that I don't bind the arraycontroller to the appdelegate, but to the viewcontroller of the sheet (the file's owner).  The objects in my search array are dictionaries, so in the Attributes panel for the array controller, I added the keys of the dict I want to bind my table to. This works fine, and the retrieved data is displayed in the table.

    But after the search when the table is displayed, awakeFromNib is called again for the sheet, and all my data is gone.  I did some debugging, and the first time it is called as follows:

    #0    0x0000000100004cff in -[SheetController awakeFromNib]
    #1    0x00007fff8ce18a41 in -[NSIBObjectData nibInstantiateWithOwner:topLevelObjects:] ()
    #2    0x00007fff8ce0ef73 in loadNib ()
    #3    0x00007fff8ce0e470 in +[NSBundle(NSNibLoading) _loadNibFile:nameTable:withZone:ownerBundle:] ()
    #4    0x00007fff8ce0e38b in +[NSBundle(NSNibLoading) loadNibFile:externalNameTable:withZone:] ()
    #5    0x00007fff8cffac34 in -[NSWindowController loadWindow] ()
    #6    0x00007fff8cffa9ef in -[NSWindowController window] ()
    #7    0x0000000100002ad0 in -[AppDelegate showSheet:]

    The second time this happens:

    #0    0x0000000100004cff in -[SheetController awakeFromNib]
    #1    0x00007fff8ce18a41 in -[NSIBObjectData nibInstantiateWithOwner:topLevelObjects:] ()
    #2    0x00007fff8cf3851b in -[NSNib instantiateNibWithExternalNameTable:] ()
    #3    0x00007fff8cf4b301 in -[NSTableRowData _unarchiveViewWithIdentifier:owner:] ()
    #4    0x00007fff8cf4b0b7 in -[NSTableRowData makeViewWithIdentifier:owner:] ()
    #5    0x00007fff8cf4afc9 in -[NSTableView makeViewWithIdentifier:owner:] ()
    #6    0x00007fff8cf4ae11 in -[NSTableView(NSTableViewViewBased) makeViewForTableColumn:row:] ()
    #7    0x00007fff8cf4aa95 in -[NSTableRowData _addViewToRowView:atColumn:row:] ()
    #8    0x00007fff8cf4a8b0 in -[NSTableRowData _addViewsToRowView:atRow:] ()
    #9    0x00007fff8cf49543 in -[NSTableRowData _addRowViewForVisibleRow:withPriorView:] ()
    #10    0x00007fff8cf49342 in -[NSTableRowData _addRowViewForVisibleRow:withPriorRowIndex:inDictionary:withRowAnimation:] ()
    #11    0x00007fff8cf49288 in -[NSTableRowData _addRowViewForVisibleRow:] ()
    #12    0x00007fff8cf489cc in -[NSTableRowData _unsafeUpdateVisibleRowEntries] ()
    #13    0x00007fff8cf487e7 in -[NSTableRowData updateVisibleRowViews] ()
    #14    0x00007fff8cee0f04 in -[NSTableView viewWillDraw] ()
    #15    0x00007fff8ce455bc in -[NSView viewWillDraw] ()
    #16    0x00007fff8ce455bc in -[NSView viewWillDraw] ()
    #17    0x00007fff8ce45e4a in -[NSScrollView viewWillDraw] ()
    #18    0x00007fff8ce455bc in -[NSView viewWillDraw] ()
    #19    0x00007fff8ce455bc in -[NSView viewWillDraw] ()
    #20    0x00007fff8ce44301 in -[NSView _sendViewWillDrawInRect:clipRootView:suppressRecursion:] ()
    #21    0x00007fff8ce43070 in -[NSView displayIfNeeded] ()
    #22    0x00007fff8ce42a2d in _handleWindowNeedsDisplayOrLayoutOrUpdateConstraints ()
    #23    0x00007fff93176f40 in __NSFireTimer ()
    #24    0x00007fff917f4934 in __CFRUNLOOP_IS_CALLING_OUT_TO_A_TIMER_CALLBACK_FUNCTION__ ()
    #25    0x00007fff917f4486 in __CFRunLoopDoTimer ()
    #26    0x00007fff917d4e11 in __CFRunLoopRun ()
    #27    0x00007fff917d4486 in CFRunLoopRunSpecific ()
    #28    0x00007fff95d794d3 in RunCurrentEventLoopInMode ()
    #29    0x00007fff95d80781 in ReceiveNextEventCommon ()
    #30    0x00007fff95d8060e in BlockUntilNextEventMatchingListInMode ()
    #31    0x00007fff8ce06e31 in _DPSNextEvent ()
    #32    0x00007fff8ce06735 in -[NSApplication nextEventMatchingMask:untilDate:inMode:dequeue:] ()
    #33    0x00007fff8ce03071 in -[NSApplication run] ()
    #34    0x00007fff8d07f244 in NSApplicationMain ()
    #35    0x0000000100002472 in main

    I can see there that the table view somehow triggers an additional nib instantiation (lines #5 -> #1). So obviously I messed up somewhere when changing from a cell to a view based table. What change could I have done (accidentally) when changing to a view based table that could cause this? Any tips for debugging this?

    Thanks,

    - Koen.
  • I solved it by using windowDidLoad instead of awakeFromNib.

    - Koen.

    On May 12, 2012, at 8:01 AM, Koen van der Drift wrote:

    > Hi,
    >
    > My app (OSX) has a sheet to obtain data from a server and display them in an NSTableView. So far I was using a Cell based table and it worked fine. Yesterday I decided to make the table view based, following the instruction by Apple <https://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/Tab
    leView/PopulatingViewTablesWithBindings/PopulatingView-TablesWithBindings.h
    tml#//apple_ref/doc/uid/10000026i-CH13-SW1
    >  The only difference with that code is that I don't bind the arraycontroller to the appdelegate, but to the viewcontroller of the sheet (the file's owner).  The objects in my search array are dictionaries, so in the Attributes panel for the array controller, I added the keys of the dict I want to bind my table to. This works fine, and the retrieved data is displayed in the table.
    >
    > But after the search when the table is displayed, awakeFromNib is called again for the sheet, and all my data is gone.  I did some debugging, and the first time it is called as follows:
    >
    > #0    0x0000000100004cff in -[SheetController awakeFromNib]
    > #1    0x00007fff8ce18a41 in -[NSIBObjectData nibInstantiateWithOwner:topLevelObjects:] ()
    > #2    0x00007fff8ce0ef73 in loadNib ()
    > #3    0x00007fff8ce0e470 in +[NSBundle(NSNibLoading) _loadNibFile:nameTable:withZone:ownerBundle:] ()
    > #4    0x00007fff8ce0e38b in +[NSBundle(NSNibLoading) loadNibFile:externalNameTable:withZone:] ()
    > #5    0x00007fff8cffac34 in -[NSWindowController loadWindow] ()
    > #6    0x00007fff8cffa9ef in -[NSWindowController window] ()
    > #7    0x0000000100002ad0 in -[AppDelegate showSheet:]
    >
    >
    >
    > The second time this happens:
    >
    > #0    0x0000000100004cff in -[SheetController awakeFromNib]
    > #1    0x00007fff8ce18a41 in -[NSIBObjectData nibInstantiateWithOwner:topLevelObjects:] ()
    > #2    0x00007fff8cf3851b in -[NSNib instantiateNibWithExternalNameTable:] ()
    > #3    0x00007fff8cf4b301 in -[NSTableRowData _unarchiveViewWithIdentifier:owner:] ()
    > #4    0x00007fff8cf4b0b7 in -[NSTableRowData makeViewWithIdentifier:owner:] ()
    > #5    0x00007fff8cf4afc9 in -[NSTableView makeViewWithIdentifier:owner:] ()
    > #6    0x00007fff8cf4ae11 in -[NSTableView(NSTableViewViewBased) makeViewForTableColumn:row:] ()
    > #7    0x00007fff8cf4aa95 in -[NSTableRowData _addViewToRowView:atColumn:row:] ()
    > #8    0x00007fff8cf4a8b0 in -[NSTableRowData _addViewsToRowView:atRow:] ()
    > #9    0x00007fff8cf49543 in -[NSTableRowData _addRowViewForVisibleRow:withPriorView:] ()
    > #10    0x00007fff8cf49342 in -[NSTableRowData _addRowViewForVisibleRow:withPriorRowIndex:inDictionary:withRowAnimation:] ()
    > #11    0x00007fff8cf49288 in -[NSTableRowData _addRowViewForVisibleRow:] ()
    > #12    0x00007fff8cf489cc in -[NSTableRowData _unsafeUpdateVisibleRowEntries] ()
    > #13    0x00007fff8cf487e7 in -[NSTableRowData updateVisibleRowViews] ()
    > #14    0x00007fff8cee0f04 in -[NSTableView viewWillDraw] ()
    > #15    0x00007fff8ce455bc in -[NSView viewWillDraw] ()
    > #16    0x00007fff8ce455bc in -[NSView viewWillDraw] ()
    > #17    0x00007fff8ce45e4a in -[NSScrollView viewWillDraw] ()
    > #18    0x00007fff8ce455bc in -[NSView viewWillDraw] ()
    > #19    0x00007fff8ce455bc in -[NSView viewWillDraw] ()
    > #20    0x00007fff8ce44301 in -[NSView _sendViewWillDrawInRect:clipRootView:suppressRecursion:] ()
    > #21    0x00007fff8ce43070 in -[NSView displayIfNeeded] ()
    > #22    0x00007fff8ce42a2d in _handleWindowNeedsDisplayOrLayoutOrUpdateConstraints ()
    > #23    0x00007fff93176f40 in __NSFireTimer ()
    > #24    0x00007fff917f4934 in __CFRUNLOOP_IS_CALLING_OUT_TO_A_TIMER_CALLBACK_FUNCTION__ ()
    > #25    0x00007fff917f4486 in __CFRunLoopDoTimer ()
    > #26    0x00007fff917d4e11 in __CFRunLoopRun ()
    > #27    0x00007fff917d4486 in CFRunLoopRunSpecific ()
    > #28    0x00007fff95d794d3 in RunCurrentEventLoopInMode ()
    > #29    0x00007fff95d80781 in ReceiveNextEventCommon ()
    > #30    0x00007fff95d8060e in BlockUntilNextEventMatchingListInMode ()
    > #31    0x00007fff8ce06e31 in _DPSNextEvent ()
    > #32    0x00007fff8ce06735 in -[NSApplication nextEventMatchingMask:untilDate:inMode:dequeue:] ()
    > #33    0x00007fff8ce03071 in -[NSApplication run] ()
    > #34    0x00007fff8d07f244 in NSApplicationMain ()
    > #35    0x0000000100002472 in main
    >
    > I can see there that the table view somehow triggers an additional nib instantiation (lines #5 -> #1). So obviously I messed up somewhere when changing from a cell to a view based table. What change could I have done (accidentally) when changing to a view based table that could cause this? Any tips for debugging this?
    >
    > Thanks,
    >
    > - Koen.
    >
    >
  • On May 12, 2012, at 5:01 AM, Koen van der Drift wrote:

    > But after the search when the table is displayed, awakeFromNib is called again for the sheet, and all my data is gone.

    -awakeFromNib is called for each object in a loaded nib … including the nib 'owner' object. That means it's possible for it to be called multiple times. It looks like what happens in your case is that the first call comes when the window nib is loaded, and the second when the nib containing the view for the table loads (probably because your controller is its owner?)

    > I solved it by using windowDidLoad instead of awakeFromNib.

    That's the best solution, since the meaning of -windowDidLoad is more specific.

    —Jens
  • On May 12, 2012, at 6:26 AM, Koen van der Drift <koenvanderdrift...> wrote:

    > I solved it by using windowDidLoad instead of awakeFromNib.

    It would be good for you to understand why this is happening.

    When building a view-based table in IB, the NSTableCellView instances you see are actually contained within embedded nibs. This makes sense, because nibs are the standard mechanism by which Cocoa archives and instantiates view instances.

    The standard -tableView:viewForTableColumn:row: boilerplate delegate method just calls -makeViewWithIdentifier:owner: on the table view to unarchive the desired table cell view from the table view's embedded nib. When you use bindings with a view-based table view, *and* you use the same identifier for the table cell view as you do for an NSTableColumn, NSTableView can save you from having to implement this method, and just passes the table column's identifier and the delegate as the File's Owner.

    But the key is the 'owner' argument. All nibs need a File's Owner, and on Mac OS X this object is sent -awakeFromNib as part of the nib loading. This is different from iOS, which does not send -awakeFromNib to File's Owner.

    Since NSTableView's convenience implementation needs to pass an owner, it passes the delegate. This is what most boilerplate implementations do as well. But the delegate object itself almost certainly lives in or owns the nib that the table view lives in. It will have already received -awakeFromNib during the instantiation of this master nib. The commonality of this pattern is why the NSTableView documentation specifically warns about this scenario.

    Your solution of moving your "master nib did load" code to -windowDidLoad is a wise one. In general, I've started to favor moving all code out of NSWindowController -awakeFromNib into -windowDidLoad, and only using -awakeFromNib for initializing objects that themselves live in nibs. It would be very nice if NSViewController had a matching -viewDidLoad (as it does on iOS), since unlike window controllers, view controllers are quite likely to live inside a nib themselves and this be ripe for *triple* invocations of -awakeFromNib (once when their containing nib loads, once when the nib they own loads, and once for each table cell view nib is instantiated).

    --Kyle Sluder

    >
    > - Koen.
    >
    >
    > On May 12, 2012, at 8:01 AM, Koen van der Drift wrote:
    >
    >> Hi,
    >>
    >> My app (OSX) has a sheet to obtain data from a server and display them in an NSTableView. So far I was using a Cell based table and it worked fine. Yesterday I decided to make the table view based, following the instruction by Apple <https://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/Tab
    leView/PopulatingViewTablesWithBindings/PopulatingView-TablesWithBindings.h
    tml#//apple_ref/doc/uid/10000026i-CH13-SW1
    >  The only difference with that code is that I don't bind the arraycontroller to the appdelegate, but to the viewcontroller of the sheet (the file's owner).  The objects in my search array are dictionaries, so in the Attributes panel for the array controller, I added the keys of the dict I want to bind my table to. This works fine, and the retrieved data is displayed in the table.
    >>
    >> But after the search when the table is displayed, awakeFromNib is called again for the sheet, and all my data is gone.  I did some debugging, and the first time it is called as follows:
    >>
    >> #0    0x0000000100004cff in -[SheetController awakeFromNib]
    >> #1    0x00007fff8ce18a41 in -[NSIBObjectData nibInstantiateWithOwner:topLevelObjects:] ()
    >> #2    0x00007fff8ce0ef73 in loadNib ()
    >> #3    0x00007fff8ce0e470 in +[NSBundle(NSNibLoading) _loadNibFile:nameTable:withZone:ownerBundle:] ()
    >> #4    0x00007fff8ce0e38b in +[NSBundle(NSNibLoading) loadNibFile:externalNameTable:withZone:] ()
    >> #5    0x00007fff8cffac34 in -[NSWindowController loadWindow] ()
    >> #6    0x00007fff8cffa9ef in -[NSWindowController window] ()
    >> #7    0x0000000100002ad0 in -[AppDelegate showSheet:]
    >>
    >>
    >>
    >> The second time this happens:
    >>
    >> #0    0x0000000100004cff in -[SheetController awakeFromNib]
    >> #1    0x00007fff8ce18a41 in -[NSIBObjectData nibInstantiateWithOwner:topLevelObjects:] ()
    >> #2    0x00007fff8cf3851b in -[NSNib instantiateNibWithExternalNameTable:] ()
    >> #3    0x00007fff8cf4b301 in -[NSTableRowData _unarchiveViewWithIdentifier:owner:] ()
    >> #4    0x00007fff8cf4b0b7 in -[NSTableRowData makeViewWithIdentifier:owner:] ()
    >> #5    0x00007fff8cf4afc9 in -[NSTableView makeViewWithIdentifier:owner:] ()
    >> #6    0x00007fff8cf4ae11 in -[NSTableView(NSTableViewViewBased) makeViewForTableColumn:row:] ()
    >> #7    0x00007fff8cf4aa95 in -[NSTableRowData _addViewToRowView:atColumn:row:] ()
    >> #8    0x00007fff8cf4a8b0 in -[NSTableRowData _addViewsToRowView:atRow:] ()
    >> #9    0x00007fff8cf49543 in -[NSTableRowData _addRowViewForVisibleRow:withPriorView:] ()
    >> #10    0x00007fff8cf49342 in -[NSTableRowData _addRowViewForVisibleRow:withPriorRowIndex:inDictionary:withRowAnimation:] ()
    >> #11    0x00007fff8cf49288 in -[NSTableRowData _addRowViewForVisibleRow:] ()
    >> #12    0x00007fff8cf489cc in -[NSTableRowData _unsafeUpdateVisibleRowEntries] ()
    >> #13    0x00007fff8cf487e7 in -[NSTableRowData updateVisibleRowViews] ()
    >> #14    0x00007fff8cee0f04 in -[NSTableView viewWillDraw] ()
    >> #15    0x00007fff8ce455bc in -[NSView viewWillDraw] ()
    >> #16    0x00007fff8ce455bc in -[NSView viewWillDraw] ()
    >> #17    0x00007fff8ce45e4a in -[NSScrollView viewWillDraw] ()
    >> #18    0x00007fff8ce455bc in -[NSView viewWillDraw] ()
    >> #19    0x00007fff8ce455bc in -[NSView viewWillDraw] ()
    >> #20    0x00007fff8ce44301 in -[NSView _sendViewWillDrawInRect:clipRootView:suppressRecursion:] ()
    >> #21    0x00007fff8ce43070 in -[NSView displayIfNeeded] ()
    >> #22    0x00007fff8ce42a2d in _handleWindowNeedsDisplayOrLayoutOrUpdateConstraints ()
    >> #23    0x00007fff93176f40 in __NSFireTimer ()
    >> #24    0x00007fff917f4934 in __CFRUNLOOP_IS_CALLING_OUT_TO_A_TIMER_CALLBACK_FUNCTION__ ()
    >> #25    0x00007fff917f4486 in __CFRunLoopDoTimer ()
    >> #26    0x00007fff917d4e11 in __CFRunLoopRun ()
    >> #27    0x00007fff917d4486 in CFRunLoopRunSpecific ()
    >> #28    0x00007fff95d794d3 in RunCurrentEventLoopInMode ()
    >> #29    0x00007fff95d80781 in ReceiveNextEventCommon ()
    >> #30    0x00007fff95d8060e in BlockUntilNextEventMatchingListInMode ()
    >> #31    0x00007fff8ce06e31 in _DPSNextEvent ()
    >> #32    0x00007fff8ce06735 in -[NSApplication nextEventMatchingMask:untilDate:inMode:dequeue:] ()
    >> #33    0x00007fff8ce03071 in -[NSApplication run] ()
    >> #34    0x00007fff8d07f244 in NSApplicationMain ()
    >> #35    0x0000000100002472 in main
    >>
    >> I can see there that the table view somehow triggers an additional nib instantiation (lines #5 -> #1). So obviously I messed up somewhere when changing from a cell to a view based table. What change could I have done (accidentally) when changing to a view based table that could cause this? Any tips for debugging this?
    >>
    >> Thanks,
    >>
    >> - Koen.
    >>
    >>
    >
  • On May 12, 2012, at 3:53 PM, Kyle Sluder wrote:

    > t would be good for you to understand why this is happening.
    >
    > When building a view-based table in IB, the NSTableCellView instances you see are actually contained within embedded nibs. This makes sense, because nibs are the standard mechanism by which Cocoa archives and instantiates view instances.

    Yes, I discovered that as well by putting a NSLog() inside awakeFromNib, it fired about eight times, which is the number of views that are visible in the tableview.

    Glad I choose wisely to solve it.  I also found the WWDC 2011 video on View based tableviews, which has a lot of info. I am still struggling a bit with the layout and appearance of all the textviews inside the NSTableCellView, but I'm sure I'll get that figured out.

    - Koen.
previous month may 2012 next month
MTWTFSS
  1 2 3 4 5 6
7 8 9 10 11 12 13
14 15 16 17 18 19 20
21 22 23 24 25 26 27
28 29 30 31      
Go to today