Rixstep
 About | ACP | Buy | Industry Watch | Learning Curve | News | Products | Search | Substack
Home » Learning Curve » Developers Workshop

Index Sets

The sky falls when the obvious becomes remote.


Get It

Try It

There's one big difference between Apple's table views and Microsoft's: Apple's don't retain properties the same way and they don't recognise the 'row' as all that important.

Microsoft's table view rows retain properties such as 'focused' and 'selected'. These properties adhere to the row itself and not the temporary index. Apple's do not.

This becomes a significant problem when refreshing views and retaining selection. Refreshing implies there might be a change in the actual order of rows, some rows can disappear, other rows can turn up for the first time.

On Windows this is no issue. But on OS X it's generally regarded as insurmountable. Rixstep are about the only software provider that accomplish selection retention on refreshes. And it's not easy.

The classic argument against doing things better probably boils down to two things. Apple code seems to assume the column is the only important thing - the column is given a class of its own: NSTableColumn. In the cell in this column is a text field but the column itself holds to the NSTableColumn class. There is no NSTableRow. But there should be.

OO fanatics will try to argue that the client application must be the data source. But this doesn't hold for refreshes and it also sidesteps the responsibilities of the view in 'MVC' in a rather lame way.

Focus and selection of table view rows is solely between user and view. The model is not involved at all. That makes it obvious the designers of Apple's table view missed something endemically important. The user focuses and selects rows - and yet after a refresh - another user action - all focus and selection is gone. Because the user controls it - not the model. To achieve any semblance of professionalism, the model has to take on the part of the view - which directly contravenes the 'MVC' the OO fanatics hold so dear.

Check any of your Cocoa applications and you'll probably find not a one outside the Rixstep fold that retains selections on refreshes. Or keeps *the* selected row in view.

Some applications - Transmit's the classic - foolishly equate file name with identity when the only true test for identity is device number together with inode. One can change directories and find items with the same names selected. (And they're not scrolled into view either. Amateurish.)

And then there's the matter of 'focus'. Apple's API is very murky on this point. There's no official recognition of focus but it has to be there.

On Windows a table row can show a dotted or dashed focus rectangle without the row being selected. There is no such thing on OS X. Yet the concept must be there.

Apple have APIs for getting at both the 'selected rows' and *the* selected row. Only one row may be 'focused' at a time - this by definition on all platforms. The unfortunately named Apple concept of *the* selected row corresponds to the row with 'focus'.

The proper - the decent - solution is to give table view rows their own properties. So when the rows are redisplayed they retain their previous selection and focus. As Apple's API ignores this necessity, one has to make it work on one's own.

The method is both simple and lugubrious. On any refresh, the client must first somehow make a note of the actual data records representing the selected rows and the 'focused' row. This is used later.

Then all rows are deselected, the sort takes place, and then rows can be selected and focused again.

Because this involves calls to the table view and because each of these calls results in the table view actually doing something with the visual interface - doing something it should be doing itself - it gets CPU-intensive and it can take a long time.

Test the 'old' Xfile with the infamous directory /usr/share/man/man3. Most people couldn't dream of 'selecting all' in there with 8,468 items - but go ahead. It will take a while to complete a refresh if all rows are first selected.

This is because each of those 8,468 rows has to first be checked for focus and selection before the refresh takes place so the data can be used again once the refresh is completed.

The client code doesn't take a lot of CPU - it's the Apple table view code that melts the CPU.

Now if there were a way to tell Apple table views in one fell swoop 'select all these rows at once'. The actual selection process might be a bit time consuming but that back and forth messaging to tell it 8,469 times to 'select' or 'focus' is gone.

But there is a way - using NSIndexSet which is just a collection of numbers. Unsigned 64-bit integers on Snow Leopard but still and all. You send the complete array all at once and that's it. Apple have in fact deprecated the earlier integer based method - they want everyone to use the NSIndexSet method instead.

The method's lugubrious for smaller application domains - you have two messaging calls where you previously had one, so there are diminishing returns on small data sets - but for Xfile it's perfect. Try the new Xfile on /usr/share/man/man3 and you'll see. A refresh with all rows selected takes about one second.

Two Days Later

That at any rate was the status quo two days ago. But by accident we compared with earlier builds for 10.4 and 10.5 - and lo and behold: there wasn't any delay at all - on the contrary. 10.6's man3 with 8,468 files all selected refreshes just as fast with the old versions as with the new one for 10.6. And that has to lead to one inevitable conclusion.

Apple programmers have fucked up the code again.

There are going to be two theories about this. The first is going to be a conspiracy theory - namely that Apple, beset with reluctance from third party to adopt the new lugubrious API, decided to sabotage the old one to force people to finally adopt it.

That's a bit of a stretch but it's not impossible.

The more accessible theory is that they've performed another 'Keystone Kops' revision, rewiring the traditional API into the new one. Which of course will take a lot more CPU - after all, it's an additional Objective-C message for each one sent. Or twice the message calls. Which of course is patently ridiculous.

And so once more we find ourselves in the curious situation where we're ultimately applying band-aids to Apple's flawed code - code that used to work well before it came from NeXT to Cupertino.

It's not likely Apple programmers themselves are suffering from this. Apple have two well known propensities that make them rather unique in the software industry.

1. Deprecate older APIs for no reason whatsover. A perfect example is how they've begun to eliminate all traditional NeXTSTEP file APIs because someone somewhere's decided everything should be in URL form instead. And from that point on one works towards 'consistency'; it never occurs to them that perhaps they should try out a new API for a while to see how it fares before eliminating the old one? Apple love painting themselves and all their system code into corners - look what happened with 'MacOS'.

2. Change things with no regard for how third party fare. To Apple, there is no third party software development, there has never been and there never will be. On other platforms there's a recognition that third party keeps things alive; for Apple, third party exists under their protest - they really don't like third party, never have, never will.

About | ACP | Buy | Industry Watch | Learning Curve | News | Products | Search | Substack
Copyright © Rixstep. All rights reserved.