-outlineView:heightOfRowByItem: gets an item of NULL??

  • My outline view can occasionally get multi-line entries, so I've
    implemented -outlineView:heightOfRowByItem: in the delegate. Normally
    this works like a charm.

    Now I'm working on some code to support dragging and dropping items
    between one outline view and another outline view.  (For what it's
    worth, none of the items in this particular outline view are using a
    nonstandard height in this case.) Suddenly, I've found that if I start
    dragging items back and forth, after about ten tries or so,
    -outlineView:heightOfRowByItem: is called with an item of "nil"!! I
    haven't narrowed down a distinct pattern yet.

    This causes an assertion to fire in my code because the first thing I do
    is assert that [item isKindOfClass:[MyItem class]]. This assertion
    hasn't ever gone off before.

    Is it expected that this delegate method could occasionally be called
    with a nil item?

    As a workaround, should I just return [outlineView rowHeight] if I get nil?
  • Although the docs don't mention it for this method, quite often an
    item of nil is used to represent the tree's root object. I would
    suggest therefore returning the default height of a table cell.

    Mike.

    On 21 Feb 2008, at 19:32, John Stiles wrote:

    > My outline view can occasionally get multi-line entries, so I've
    > implemented -outlineView:heightOfRowByItem: in the delegate.
    > Normally this works like a charm.
    >
    > Now I'm working on some code to support dragging and dropping items
    > between one outline view and another outline view.  (For what it's
    > worth, none of the items in this particular outline view are using a
    > nonstandard height in this case.) Suddenly, I've found that if I
    > start dragging items back and forth, after about ten tries or so, -
    > outlineView:heightOfRowByItem: is called with an item of "nil"!! I
    > haven't narrowed down a distinct pattern yet.
    >
    > This causes an assertion to fire in my code because the first thing
    > I do is assert that [item isKindOfClass:[MyItem class]]. This
    > assertion hasn't ever gone off before.
    >
    > Is it expected that this delegate method could occasionally be
    > called with a nil item?
    >
    > As a workaround, should I just return [outlineView rowHeight] if I
    > get nil?
  • Fair enough, but the root item is never represented graphically in a
    table, so there wouldn't be a need to ask for its row height…?
    Anyway, I filed a radar… we'll see what happens.

    Mike Abdullah wrote:
    > Although the docs don't mention it for this method, quite often an
    > item of nil is used to represent the tree's root object. I would
    > suggest therefore returning the default height of a table cell.
    >
    > Mike.
    >
    > On 21 Feb 2008, at 19:32, John Stiles wrote:
    >
    >> My outline view can occasionally get multi-line entries, so I've
    >> implemented -outlineView:heightOfRowByItem: in the delegate. Normally
    >> this works like a charm.
    >>
    >> Now I'm working on some code to support dragging and dropping items
    >> between one outline view and another outline view.  (For what it's
    >> worth, none of the items in this particular outline view are using a
    >> nonstandard height in this case.) Suddenly, I've found that if I
    >> start dragging items back and forth, after about ten tries or so,
    >> -outlineView:heightOfRowByItem: is called with an item of "nil"!! I
    >> haven't narrowed down a distinct pattern yet.
    >>
    >> This causes an assertion to fire in my code because the first thing I
    >> do is assert that [item isKindOfClass:[MyItem class]]. This assertion
    >> hasn't ever gone off before.
    >>
    >> Is it expected that this delegate method could occasionally be called
    >> with a nil item?
    >>
    >> As a workaround, should I just return [outlineView rowHeight] if I
    >> get nil?
    >
  • It shouldn't ask you for the height of a NULL item.

    This is either:
    1. a bug in nsoutlineview
    2. your code may be reloading the nsoutlineview in one of the delegate
    methods, putting it into a "strange" state. If you see this happening
    consistently, you may want to break on NSOutlineView reloadData and
    see if it is getting called when it shouldn't be.

    corbin

    On Feb 21, 2008, at 12:06 PM, John Stiles wrote:

    > Fair enough, but the root item is never represented graphically in a
    > table, so there wouldn't be a need to ask for its row height…?
    > Anyway, I filed a radar… we'll see what happens.
    >
    >
    > Mike Abdullah wrote:
    >> Although the docs don't mention it for this method, quite often an
    >> item of nil is used to represent the tree's root object. I would
    >> suggest therefore returning the default height of a table cell.
    >>
    >> Mike.
    >>
  • Is it illegal to call -reloadData from inside a delegate method?

    Because, yeah, when -outlineView:acceptDrop:item:childIndex: is called,
    that could easily lead to the whole contents of the outline view getting
    rebuilt from scratch. And this could definitely lead to calls to
    -reloadData or -reloadItem:reloadChildren:.

    If this is going to bite me down the line, what are my options? Sadly,
    it's not possible to defer method calls until after
    -outlineView:acceptDrop:item:childIndex: returns, due to an AppKit bug:

        rdar://5686701    Drag-and-drop from an outline view runs a
    run-loop in NSDefaultRunLoopMode

    So I don't really know how to work around this bug without going to
    extreme lengths.

    Please go into a little more detail about what a delegate method can or
    cannot do. Right now it's entirely possible, in some corner cases, that
    small things like selection-change events (e.g.
    -outlineViewSelectionDidChange:) could lead to huge ripple effects which
    end up causing the whole window to rebuild itself from scratch. If
    that's not kosher, I need to seriously reevaluate a lot of code.

    Corbin Dunn wrote:
    > It shouldn't ask you for the height of a NULL item.
    >
    > This is either:
    > 1. a bug in nsoutlineview
    > 2. your code may be reloading the nsoutlineview in one of the
    delegate methods, putting it into a "strange" state. If you see this
    happening consistently, you may want to break on NSOutlineView
    reloadData and see if it is getting called when it shouldn't be.
    >
    > corbin
    >
    > On Feb 21, 2008, at 12:06 PM, John Stiles wrote:
    >
    >> Fair enough, but the root item is never represented graphically in a
    table, so there wouldn't be a need to ask for its row height…?
    >> Anyway, I filed a radar… we'll see what happens.
    >>
    >>
    >> Mike Abdullah wrote:
    >>> Although the docs don't mention it for this method, quite often an
    item of nil is used to represent the tree's root object. I would suggest
    therefore returning the default height of a table cell.
    >>>
    >>> Mike.
    >>>
  • On Feb 22, 2008, at 11:53 AM, John Stiles wrote:

    > Is it illegal to call -reloadData from inside a delegate method?

    It depends where. If you call -reloadData when -
    numberOfChildrenOfItem: is called, then it will mess things up
    (basically, that is true for any of the required methods). Basically,
    you can imagine why:

    for (int i = 0; i < child count; i++) {
      (call delegate)
      //if it reloaded, then child count may have changed...that can
    cause trouble.
    }

    For the non-required ones, it is less essential. For acceptDrop, it
    should be okay, but note that the NSOutlineView doesn't retain the
    items, so you'll have to keep that in consideration. Selection change
    is also okay. Sorry for the confusion -- I should have been more
    specific. Your cases sound okay.

    corbin

    >
    >
    > Because, yeah, when -outlineView:acceptDrop:item:childIndex: is
    > called,  that could easily lead to the whole contents of the outline
    > view getting rebuilt from scratch. And this could definitely lead to
    > calls to -reloadData or -reloadItem:reloadChildren:.
    >
    > If this is going to bite me down the line, what are my options?
    > Sadly, it's not possible to defer method calls until after -
    > outlineView:acceptDrop:item:childIndex: returns, due to an AppKit bug:
    >
    > rdar://5686701    Drag-and-drop from an outline view runs a run-
    > loop in NSDefaultRunLoopMode
    >
    > So I don't really know how to work around this bug without going to
    > extreme lengths.
    >
    > Please go into a little more detail about what a delegate method can
    > or cannot do. Right now it's entirely possible, in some corner
    > cases, that small things like selection-change events (e.g. -
    > outlineViewSelectionDidChange:) could lead to huge ripple effects
    > which end up causing the whole window to rebuild itself from
    > scratch. If that's not kosher, I need to seriously reevaluate a lot
    > of code.
    >
    >
    > Corbin Dunn wrote:
    >> It shouldn't ask you for the height of a NULL item.
    >>
    >> This is either:
    >> 1. a bug in nsoutlineview
    >> 2. your code may be reloading the nsoutlineview in one of the
    > delegate methods, putting it into a "strange" state. If you see this
    > happening consistently, you may want to break on NSOutlineView
    > reloadData and see if it is getting called when it shouldn't be.
    >>
    >> corbin
    >>
    >> On Feb 21, 2008, at 12:06 PM, John Stiles wrote:
    >>
    >>> Fair enough, but the root item is never represented graphically
    > in a table, so there wouldn't be a need to ask for its row height…?
    >>> Anyway, I filed a radar… we'll see what happens.
    >>>
    >>>
    >>> Mike Abdullah wrote:
    >>>> Although the docs don't mention it for this method, quite often
    > an item of nil is used to represent the tree's root object. I would
    > suggest therefore returning the default height of a table cell.
    >>>>
    >>>> Mike.
    >>>>
    >
  • OK, that makes sense. My -outlineView:numberOfChildrenOfItem: doesn't do
    anything major, just returns the count of an array. I don't foresee any
    problems there.

    Should I file a doc enhancement bug on this, so this can get in the
    docs? The -outlineView:numberOfChildrenOfItem: docs should really have a
    warning here, and if there are any other cases, they should be
    documented too.

    If you can think of any other cases where it would be bad for
    -reloadData to get called in a delegate, let me know. I would like to
    see it.

    Also, would you like a call stack of my NULL-item bug? Would that help
    for diagnosing it?

    Corbin Dunn wrote:
    >
    > On Feb 22, 2008, at 11:53 AM, John Stiles wrote:
    >
    >> Is it illegal to call -reloadData from inside a delegate method?
    >
    > It depends where. If you call -reloadData when
    > -numberOfChildrenOfItem: is called, then it will mess things up
    > (basically, that is true for any of the required methods). Basically,
    > you can imagine why:
    >
    > for (int i = 0; i < child count; i++) {
    > (call delegate)
    > //if it reloaded, then child count may have changed...that can cause
    > trouble.
    > }
    >
    > For the non-required ones, it is less essential. For acceptDrop, it
    > should be okay, but note that the NSOutlineView doesn't retain the
    > items, so you'll have to keep that in consideration. Selection change
    > is also okay. Sorry for the confusion -- I should have been more
    > specific. Your cases sound okay.
    >
    > corbin
    >
    >
    >>
    >>
    >> Because, yeah, when -outlineView:acceptDrop:item:childIndex: is
    >> called,  that could easily lead to the whole contents of the outline
    >> view getting rebuilt from scratch. And this could definitely lead to
    >> calls to -reloadData or -reloadItem:reloadChildren:.
    >>
    >> If this is going to bite me down the line, what are my options?
    >> Sadly, it's not possible to defer method calls until after
    >> -outlineView:acceptDrop:item:childIndex: returns, due to an AppKit bug:
    >>
    >> rdar://5686701    Drag-and-drop from an outline view runs a
    >> run-loop in NSDefaultRunLoopMode
    >>
    >> So I don't really know how to work around this bug without going to
    >> extreme lengths.
    >>
    >> Please go into a little more detail about what a delegate method can
    >> or cannot do. Right now it's entirely possible, in some corner cases,
    >> that small things like selection-change events (e.g.
    >> -outlineViewSelectionDidChange:) could lead to huge ripple effects
    >> which end up causing the whole window to rebuild itself from scratch.
    >> If that's not kosher, I need to seriously reevaluate a lot of code.
    >>
    >>
    >> Corbin Dunn wrote:
    >>> It shouldn't ask you for the height of a NULL item.
    >>>
    >>> This is either:
    >>> 1. a bug in nsoutlineview
    >>> 2. your code may be reloading the nsoutlineview in one of the
    >> delegate methods, putting it into a "strange" state. If you see this
    >> happening consistently, you may want to break on NSOutlineView
    >> reloadData and see if it is getting called when it shouldn't be.
    >>>
    >>> corbin
    >>>
    >>> On Feb 21, 2008, at 12:06 PM, John Stiles wrote:
    >>>
    >>>> Fair enough, but the root item is never represented graphically in
    >> a table, so there wouldn't be a need to ask for its row height…?
    >>>> Anyway, I filed a radar… we'll see what happens.
    >>>>
    >>>>
    >>>> Mike Abdullah wrote:
    >>>>> Although the docs don't mention it for this method, quite often
    >> an item of nil is used to represent the tree's root object. I would
    >> suggest therefore returning the default height of a table cell.
    >>>>>
    >>>>> Mike.
    >>>>>
    >>
    >
  • Yes to everything, thanks!

    corbin

    On Feb 22, 2008, at 12:15 PM, John Stiles wrote:

    > OK, that makes sense. My -outlineView:numberOfChildrenOfItem:
    > doesn't do anything major, just returns the count of an array. I
    > don't foresee any problems there.
    >
    > Should I file a doc enhancement bug on this, so this can get in the
    > docs? The -outlineView:numberOfChildrenOfItem: docs should really
    > have a warning here, and if there are any other cases, they should
    > be documented too.
    >
    > If you can think of any other cases where it would be bad for -
    > reloadData to get called in a delegate, let me know. I would like to
    > see it.
    >
    > Also, would you like a call stack of my NULL-item bug? Would that
    > help for diagnosing it?
    >
    >
    > Corbin Dunn wrote:
    >>
    >> On Feb 22, 2008, at 11:53 AM, John Stiles wrote:
    >>
    >>> Is it illegal to call -reloadData from inside a delegate method?
    >>
    >> It depends where. If you call -reloadData when -
    >> numberOfChildrenOfItem: is called, then it will mess things up
    >> (basically, that is true for any of the required methods).
    >> Basically, you can imagine why:
    >>
    >> for (int i = 0; i < child count; i++) {
    >> (call delegate)
    >> //if it reloaded, then child count may have changed...that can
    >> cause trouble.
    >> }
    >>
    >> For the non-required ones, it is less essential. For acceptDrop, it
    >> should be okay, but note that the NSOutlineView doesn't retain the
    >> items, so you'll have to keep that in consideration. Selection
    >> change is also okay. Sorry for the confusion -- I should have been
    >> more specific. Your cases sound okay.
    >>
    >> corbin
    >>
    >>
    >>>
    >>>
    >>> Because, yeah, when -outlineView:acceptDrop:item:childIndex: is
    >>> called,  that could easily lead to the whole contents of the
    >>> outline view getting rebuilt from scratch. And this could
    >>> definitely lead to calls to -reloadData or -
    >>> reloadItem:reloadChildren:.
    >>>
    >>> If this is going to bite me down the line, what are my options?
    >>> Sadly, it's not possible to defer method calls until after -
    >>> outlineView:acceptDrop:item:childIndex: returns, due to an AppKit
    >>> bug:
    >>>
    >>> rdar://5686701    Drag-and-drop from an outline view runs a run-
    >>> loop in NSDefaultRunLoopMode
    >>>
    >>> So I don't really know how to work around this bug without going
    >>> to extreme lengths.
    >>>
    >>> Please go into a little more detail about what a delegate method
    >>> can or cannot do. Right now it's entirely possible, in some corner
    >>> cases, that small things like selection-change events (e.g. -
    >>> outlineViewSelectionDidChange:) could lead to huge ripple effects
    >>> which end up causing the whole window to rebuild itself from
    >>> scratch. If that's not kosher, I need to seriously reevaluate a
    >>> lot of code.
    >>>
    >>>
    >>> Corbin Dunn wrote:
    >>>> It shouldn't ask you for the height of a NULL item.
    >>>>
    >>>> This is either:
    >>>> 1. a bug in nsoutlineview
    >>>> 2. your code may be reloading the nsoutlineview in one of the
    >>> delegate methods, putting it into a "strange" state. If you see
    >>> this happening consistently, you may want to break on
    >>> NSOutlineView reloadData and see if it is getting called when it
    >>> shouldn't be.
    >>>>
    >>>> corbin
    >>>>
    >>>> On Feb 21, 2008, at 12:06 PM, John Stiles wrote:
    >>>>
    >>>>> Fair enough, but the root item is never represented graphically
    >>> in a table, so there wouldn't be a need to ask for its row height…?
    >>>>> Anyway, I filed a radar… we'll see what happens.
    >>>>>
    >>>>>
    >>>>> Mike Abdullah wrote:
    >>>>>> Although the docs don't mention it for this method, quite
    >>> often an item of nil is used to represent the tree's root object.
    >>> I would suggest therefore returning the default height of a table
    >>> cell.
    >>>>>>
    >>>>>> Mike.
    >>>>>>
    >>>
    >>
  • BTW, I've noticed a pattern with the bug: it only seems to happen when I
    am dropping an item as the very first element in an already-populated
    folder.

    I've filed a radar about the docs and sent the bug number in a separate
    email.

    Here's a call stack:

                                      *** assertion ***
    #3    0x00c5abc6 in -[OsGuiTreeData outlineView:heightOfRowByItem:] at
    COsTreeView.mm:338
    #4    0x95d40709 in -[NSOutlineView _sendDelegateHeightOfRow:]
    #5    0x95d40531 in -[NSTableView _uncachedRectHeightOfRow:]
    #6    0x95d40420 in -[_NSTableRowHeightStorage
    _cacheRowHeightsIntoBucket:bucketFirstRowIndex:lastCacheableRowIndex:]
    #7    0x95d45eef in -[_NSTableRowHeightStorage
    _recomputeBucketIndex:bucketFirstRowIndex:]
    #8    0x95d45d0a in -[_NSTableRowHeightStorage insertRows:atIndex:]
    #9    0x95be3ca1 in -[NSOutlineView
    _adjustSelectionForItemEntry:numberOfRows:adjustFieldEditorIfNecessary:]
    #10    0x95d4350e in -[NSOutlineView reloadItem:reloadChildren:]
    #11    0x00c5a82c in COsTreeViewImpl::ReloadParentOfItem at
    COsTreeView.mm:813
    #12    0x00c5d3fd in COsTreeView::InsertItem at COsTreeView.mm:890

    ... in response to the drop, my code is inserting an element into the
    tree ...

    #19    0x00c5d09e in COsTreeViewImpl::HandleDropForTargetAtChildIndex at
    COsTreeView.mm:573
    #20    0x00c5d1ce in -[OsGuiTreeData
    outlineView:acceptDrop:item:childIndex:] at COsTreeView.mm:424
    #21    0x96058d90 in -[NSOutlineView performDragOperation:]
    #22    0x95e25f15 in NSCoreDragReceiveProc
    #23    0x963d055c in DoDropMessage
    #24    0x963d04d2 in SendDropMessage
    #25    0x963cd83a in DragInApplication
    #26    0x963cc2de in CoreDragStartDragging
    #27    0x95e23d9d in -[NSCoreDragManager _dragUntilMouseUp:accepted:]
    #28    0x95e22cc2 in -[NSCoreDragManager
    dragImage:fromWindow:at:offset:event:pasteboard:source:slideBack:]
    #29    0x95e2270c in -[NSWindow(NSDrag)
    dragImage:at:offset:event:pasteboard:source:slideBack:]
    #30    0x96058822 in -[NSOutlineView
    dragImage:at:offset:event:pasteboard:source:slideBack:]
    #31    0x95e22354 in -[NSTableView
    _doImageDragUsingRowsWithIndexes:event:pasteboard:source:slideBack:]
    #32    0x95c6a390 in -[NSTableView _performDragFromMouseDown:]
    #33    0x95c682ea in -[NSTableView mouseDown:]
    #34    0x95c79edf in -[NSOutlineView mouseDown:]
    #35    0x95c20ac3 in -[NSWindow sendEvent:]
    #36    0x95bed714 in -[NSApplication sendEvent:]
    #37    0x95b4b0f9 in -[NSApplication run]
    #38    0x95b1830a in NSApplicationMain
    #39    0x00007f72 in main at EditorMain.mm:62

    Corbin Dunn wrote:
    > Yes to everything, thanks!
    >
    > corbin
    >
    > On Feb 22, 2008, at 12:15 PM, John Stiles wrote:
    >
    >> OK, that makes sense. My -outlineView:numberOfChildrenOfItem: doesn't
    >> do anything major, just returns the count of an array. I don't
    >> foresee any problems there.
    >>
    >> Should I file a doc enhancement bug on this, so this can get in the
    >> docs? The -outlineView:numberOfChildrenOfItem: docs should really
    >> have a warning here, and if there are any other cases, they should be
    >> documented too.
    >>
    >> If you can think of any other cases where it would be bad for
    >> -reloadData to get called in a delegate, let me know. I would like to
    >> see it.
    >>
    >> Also, would you like a call stack of my NULL-item bug? Would that
    >> help for diagnosing it?
    >>
    >>
    >> Corbin Dunn wrote:
    >>>
    >>> On Feb 22, 2008, at 11:53 AM, John Stiles wrote:
    >>>
    >>>> Is it illegal to call -reloadData from inside a delegate method?
    >>>
    >>> It depends where. If you call -reloadData when
    >>> -numberOfChildrenOfItem: is called, then it will mess things up
    >>> (basically, that is true for any of the required methods).
    >>> Basically, you can imagine why:
    >>>
    >>> for (int i = 0; i < child count; i++) {
    >>> (call delegate)
    >>> //if it reloaded, then child count may have changed...that can
    >>> cause trouble.
    >>> }
    >>>
    >>> For the non-required ones, it is less essential. For acceptDrop, it
    >>> should be okay, but note that the NSOutlineView doesn't retain the
    >>> items, so you'll have to keep that in consideration. Selection
    >>> change is also okay. Sorry for the confusion -- I should have been
    >>> more specific. Your cases sound okay.
    >>>
    >>> corbin
    >>>
    >>>
    >>>>
    >>>>
    >>>> Because, yeah, when -outlineView:acceptDrop:item:childIndex: is
    >>>> called,  that could easily lead to the whole contents of the
    >>>> outline view getting rebuilt from scratch. And this could
    >>>> definitely lead to calls to -reloadData or
    >>>> -reloadItem:reloadChildren:.
    >>>>
    >>>> If this is going to bite me down the line, what are my options?
    >>>> Sadly, it's not possible to defer method calls until after
    >>>> -outlineView:acceptDrop:item:childIndex: returns, due to an AppKit
    >>>> bug:
    >>>>
    >>>> rdar://5686701    Drag-and-drop from an outline view runs a
    >>>> run-loop in NSDefaultRunLoopMode
    >>>>
    >>>> So I don't really know how to work around this bug without going to
    >>>> extreme lengths.
    >>>>
    >>>> Please go into a little more detail about what a delegate method
    >>>> can or cannot do. Right now it's entirely possible, in some corner
    >>>> cases, that small things like selection-change events (e.g.
    >>>> -outlineViewSelectionDidChange:) could lead to huge ripple effects
    >>>> which end up causing the whole window to rebuild itself from
    >>>> scratch. If that's not kosher, I need to seriously reevaluate a lot
    >>>> of code.
    >>>>
    >>>>
    >>>> Corbin Dunn wrote:
    >>>>> It shouldn't ask you for the height of a NULL item.
    >>>>>
    >>>>> This is either:
    >>>>> 1. a bug in nsoutlineview
    >>>>> 2. your code may be reloading the nsoutlineview in one of the
    >>>> delegate methods, putting it into a "strange" state. If you see
    >>>> this happening consistently, you may want to break on NSOutlineView
    >>>> reloadData and see if it is getting called when it shouldn't be.
    >>>>>
    >>>>> corbin
    >>>>>
    >>>>> On Feb 21, 2008, at 12:06 PM, John Stiles wrote:
    >>>>>
    >>>>>> Fair enough, but the root item is never represented graphically
    >>>> in a table, so there wouldn't be a need to ask for its row height…?
    >>>>>> Anyway, I filed a radar… we'll see what happens.
    >>>>>>
    >>>>>>
    >>>>>> Mike Abdullah wrote:
    >>>>>>> Although the docs don't mention it for this method, quite often
    >>>> an item of nil is used to represent the tree's root object. I would
    >>>> suggest therefore returning the default height of a table cell.
    >>>>>>>
    >>>>>>> Mike.
    >>>>>>>
    >>>>
    >>>
    >