NSTableView: tableView:objectValueForTableColumn:row called more than necessary when scrolling

  • Hi everyone,

    I just found that when scrolling a NSTableView inside a NSScrollView
    one row at a time, each time the tableview will not only ask the data
    source for the new row's value but also ask for the last row's. So if
    you scroll the table all the way donw, eventually it makes twice calls
    as the number of rows in the table.

    I been programming a app that performance of the table view is vital,
    so I really want the table view call the data source as few as
    possible. Does anyone know a solution?

    Thanks,
    Ben
  • Ben,

    In my app I am drawing pages of pdf files in my tables.  Drawing a pdf
    page (into an NSImage, in my case) can take a long time, depending on
    the complexity of the page.  A file with complicated pages will
    display one cell at a time with a pause after each cell.  This is
    unusable.

    My solution is to cache the NSImage of each pdf page and just retrieve
    that image, rather than drawing the pdf page each time the cell
    draws.  I use NSOperation to draw the pages in a separate thread and
    my table dataSource/delegate will have the table redraw just the cell
    of a newly available pdf page image.  Before an image is ready, the
    table just draws an empty image frame.

    Of course, you don't need to complicate things with NSOperation and
    delayed drawing.  You can just cache the table data the first time the
    cell draws.

    The docs for tableView:objectValueForTableColumn:row: say that this
    method "must be efficient" and I make mine efficient by caching my
    table data.

    Good luck.

    john

    On Jan 16, 2008, at 2:07 PM, Ben Chen wrote:

    > Hi everyone,
    >
    > I just found that when scrolling a NSTableView inside a NSScrollView
    > one row at a time, each time the tableview will not only ask the data
    > source for the new row's value but also ask for the last row's. So if
    > you scroll the table all the way donw, eventually it makes twice calls
    > as the number of rows in the table.
    >
    > I been programming a app that performance of the table view is vital,
    > so I really want the table view call the data source as few as
    > possible. Does anyone know a solution?
    >
    > Thanks,
    > Ben
  • Hi Ben,

    I suspect the designers of NSTableView decided to save themselves
    having to cache values for the currently-selected row (which would
    later be needed to redraw that row unhighlighted) and in doing so
    doomed the rest of us to have to write such code in every single
    datasource.

    Hamish

    On Jan 16, 2008 10:07 PM, Ben Chen <ben.sleeper...> wrote:
    > Hi everyone,
    >
    > I just found that when scrolling a NSTableView inside a NSScrollView
    > one row at a time, each time the tableview will not only ask the data
    > source for the new row's value but also ask for the last row's. So if
    > you scroll the table all the way donw, eventually it makes twice calls
    > as the number of rows in the table.
    >
    > I been programming a app that performance of the table view is vital,
    > so I really want the table view call the data source as few as
    > possible. Does anyone know a solution?
    >
    > Thanks,
    > Ben
    >
  • It seems like you might be able to write a once-off class which acted as
    a simple data-source cache, keeping track of the last few requests so
    they can be re-provided as necessary, and then use that anywhere you
    needed it.

    Hamish Allan wrote:
    > Hi Ben,
    >
    > I suspect the designers of NSTableView decided to save themselves
    > having to cache values for the currently-selected row (which would
    > later be needed to redraw that row unhighlighted) and in doing so
    > doomed the rest of us to have to write such code in every single
    > datasource.
    >
    > Hamish
    >
    > On Jan 16, 2008 10:07 PM, Ben Chen <ben.sleeper...> wrote:
    >
    >> Hi everyone,
    >>
    >> I just found that when scrolling a NSTableView inside a NSScrollView
    >> one row at a time, each time the tableview will not only ask the data
    >> source for the new row's value but also ask for the last row's. So if
    >> you scroll the table all the way donw, eventually it makes twice calls
    >> as the number of rows in the table.
    >>
    >> I been programming a app that performance of the table view is vital,
    >> so I really want the table view call the data source as few as
    >> possible. Does anyone know a solution?
    >>
    >> Thanks,
    >> Ben
    >>
    >>

    >
  • > I suspect the designers of NSTableView decided to save themselves
    > having to cache values for the currently-selected row (which would
    > later be needed to redraw that row unhighlighted) and in doing so
    > doomed the rest of us to have to write such code in every single
    > datasource.

    Or, the exact opposite. Why cache certain things since your datasource
    should already have them in the 'model'? The tableview is purely a
    display mechanism for the visible rows.

    As John said, this method must be fast. To quote the documentation:

    > Note:  tableView:objectValueForTableColumn:row: is called each time
    > the table cell needs to be redisplayed, so it must be efficient.

    As others have mentioned -- if your current implementation is too
    slow, then you will need to implement a proxy layer that caches the
    values.

    corbin
  • On Jan 17, 2008 6:24 PM, Corbin Dunn <corbind...> wrote:

    > Why cache certain things since your datasource should already have them in the 'model'?

    Because you know the very next fetch will always be for exactly the same data?

    Hamish
  • On Jan 17, 2008, at 10:35 AM, Hamish Allan wrote:

    > On Jan 17, 2008 6:24 PM, Corbin Dunn <corbind...> wrote:
    >
    >> Why cache certain things since your datasource should already have
    >> them in the 'model'?
    >
    > Because you know the very next fetch will always be for exactly the
    > same data?

    No, this frequently isn't true, and it is the primary reason why a
    caching model can't be directly added to NSTableView (side note,
    NSOutlineView, which has an item-based model, does cache things.
    NSTableView has a row-based model).

    People frequently do this type of thing when they want a cell to
    redraw its contents at row/column, but not redraw anything else:

    [tableView setNeedsDisplayInRect:[tableView frameOfCellAtColumn:column
    row:row]];

    The above way of redrawing a particular cell is very efficient. If we
    introduced caching, the above would no longer work.

    Of course, a caching mechanism could be introduced, but it would
    require user adoption and new API. For instance, the above issue would
    require new API to invalidate the internal cache for a given row/
    column pair.

    Feel free to log such things as feature requests for AppKit. What you
    are wanting is basically a lazy-push model (all the data is pushed
    into the view lazily) as opposed to the current lazy pull model (all
    the data is pulled when the view needs it).

    thanks,
    corbin
  • On Jan 17, 2008 6:48 PM, Corbin Dunn <corbind...> wrote:

    > Feel free to log such things as feature requests for AppKit. What you
    > are wanting is basically a lazy-push model (all the data is pushed
    > into the view lazily) as opposed to the current lazy pull model (all
    > the data is pulled when the view needs it).

    I don't want a push model: I don't want the table view to cache all
    the data it ever displays, just the data for the currently selected
    visible rows (because it is likely to need it again when the rows are
    unselected). But thinking about it a bit more, this would be rather
    inefficient when scrolling down a large table in which all the rows
    are selected, so I should probably be more careful what I wish for :)

    Thanks,
    Hamish
  • On Jan 17, 2008, at 11:16 AM, Hamish Allan wrote:

    >> Feel free to log such things as feature requests for AppKit. What you
    >> are wanting is basically a lazy-push model (all the data is pushed
    >> into the view lazily) as opposed to the current lazy pull model (all
    >> the data is pulled when the view needs it).
    >
    > I don't want a push model: I don't want the table view to cache all
    > the data it ever displays, just the data for the currently selected
    > visible rows (because it is likely to need it again when the rows are
    > unselected). But thinking about it a bit more, this would be rather
    > inefficient when scrolling down a large table in which all the rows
    > are selected, so I should probably be more careful what I wish for :)

    For that same reason, wouldn't it also be cool to have an inverse
    method to:

    tableView:objectValueForTableColumn:row:

    that looks something like this:

    tableView:invalidateObjectValueForTableColumn:row:

    that would be called whenever a particular cell is no longer visible
    in the table view, to allow the data source to purge the associated
    resource?

    j o a r
previous month january 2008 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