Friday, November 19, 2004

Cocoa Bindings

Brent Simmons just started using Cocoa Bindings:

It was, I confess, a reaction to that terrible feeling you get when you’re faced with creating yet another NSTableView data source and delegate. I just couldn’t face another objectValueForTableColumn:blah:blah:whatever:.

and he likes them. I actually had the opposite reaction a few days ago. I was excited to use bindings, as I had already made the decision that the application would require Panther. I love the idea of bindings. And, indeed, bindings made it very easy to implement the basic functionality for the table. No datasource methods! No need to sort! Automatic updates! But the last 20% looked daunting. I needed typehead, editing and insertion validation that depended on objects outside of the row, control over when editing and deletion should be enabled, standard keyboard shortcuts that aren’t implemented by the framework, etc.

I started mapping out the subclasses I would need—you have to subclass to even know what the view objects are bound to—and how they would be hooked together, and decided that I could probably get it to do what I wanted to do, but that it just wasn’t worth it. There’s no sense in fighting the framework. Sure it’s kind of silly that I have to write -numberOfRowsInTableView: yet again, but it’s also the simplest method in my class so it’s not a big deal if the framework can’t handle it automatically.

Another case where I ended up not using bindings was in a pop-up menu that needed to be populated from an array, but that also needed a few extra menu items. Bindings don’t seem to have been designed to handle that.

This is not to say that I don’t like bindings or that I won’t use them. Like -takeStringValueFrom:, they make for a great demo, and sometimes they do exactly what you want (most likely for uneditable views). But they’re not as flexible as other means and are not always the right solution. As I write new code, I will keep looking for places where it looks like bindings will help, but I have no desire to convert my old code to use them. Aside from the extra work, I am convinced that in some cases it would make the code harder to understand and slower.

A final point: it’s not bindings or nothing. Developers can build their own helper objects that work with the standard datasource and delegate methods. These can provide some of the convenience of using bindings, but with easier customizability and more room to grow.

10 Comments RSS · Twitter

This has been my experience exactly. As I've tried to use bindings, I've found some situations (such as preferences) where they are a huge win, and other situations where they would seem to complicate things needlessly. Cocoalicious uses bindings in several places, but uses the standard tableview delegate methods.

To quote a friend... "It's funny how people somehow think there's a special 'bindings' version of NSTableView or something which prevents you from doing normal things."

Erik, if that's not the case, how do you, for example, call -tableView:objectValueForTableColumn:row: on the datasource of a table that's fed by bindings?

If you're asking how, for a known row index, you retrieve a property of the object represented in that row, you could (IIRC) do:

[[[myArrayController arrangedObjects] objectAtIndex: row] valueForKey: @"myKey"];

I often find myself having an outlet to the NSArrayController that feeds my tables.

Fraser: that works, but I have some problems with it. First, it's not the datasource method. So a table using bindings is indeed special and has to be treated differently from a regular table. Second, it's not easy to know the name of the key from inside the table. You can't even ask the table column for the key unless you've subclassed the table column and set it up to record its value binding. Also, although I guess it's possible that a special, optimizing NSArray subclass is used, I'm leery of asking the array controller for all the arrangedObjects when I only need a specific row. WIth the datasource method, I can directly ask for the row.

Michael, that's one of the most rational opinions about bindings I've heard so far. Bottom line for me: great applications cannot be build with zero lines of code. I'll try bindings when I can require Panther (not anytime soon), but I'm not expecting much.

For your second case, you could just subclass NSArrayController and override -arrangedObjects to always return [super arrangedObjects] followed by your extra items.

I don't know the details of your first case so I can't help you there.

It's unclear to me why you would want to go through a table view's data source to find a value rather than ask the model object directly. You probably have a specific case in mind that's just not occurring to me.

Chris: the extra items have different actions than the main items, so just getting them in the arrangedObjects array isn't enough, I think.

The reason I want to go through the datasource is that I already have model-independent code in a table subclass. One example is code that copies the contents of the table, as displayed by the formatters, to the pasteboard. If I don't go through the datasource, I have to (a) write this code again, and (b) somehow get access to the proper model keys from inside the table.

Admittedly, some of my problems with bindings, as they relate to table views, may have to do with a fundamental lack of understanding. I'm working on it.

I've actually had to pull bindings out of two applications due to performance and debugability issues. Read the long sad story in my news feed: http://istumbler.net/feeds/news.rss

Leave a Comment