When we were designing the MonoTouch APIs we had to map
some of the constructs used by CocoaTouch and Objective-C to
what C# developer expect.
Like other modern toolkits, the behavior of a control can
be customized by the programmer by hooking up to events and the
responses to those events.
This is a common pattern used in other toolkits. In Gtk+
objects emit signals, and the programmer can connect event
handlers to individual signals, for example this is how you
would connect
void pause_player ()
{
// pause the player.
}
void configure_pause ()
{
button = gtk_button_new_with_label ("pause");
gtk_signal_connect (button, "clicked", pause_player, NULL);
}
In the ECMA CLI world developers hook up to events by
connecting to events directly in the object, it is similar in
spirit:
void configure_pause ()
{
button = new Button ("Pause");
button.Clicked += delegate {
// Pause the player here.
};
}
In both the Gtk+ and the ECMA CLI worlds there can be
multiple subscribers to the event.
In CocoaTouch, instead of having an event per action they
use a pattern where the components emits all of its messages
to an instance of an object. If a button were to emit
messages like "clicked", "clicked_violently" and
"caption_changed" all of those messages will be sent to an
object that implemented the required interface.
I am going to give myself a literary license and use some
terms loosely and write a little bit of imaginary C#.
A button implementation would look like this:
interface ButtonDelegate {
optional void clicked ();
optional void clicked_violently ();
optional void caption_changed ();
}
class Button {
public ButtonDelegate Delegate { get; set; }
public Button (string text) { ... }
public string Text {
get { return text; }
set {
text = value;
DoSomeRepainting ();
Delegate.caption_changed ();
}
}
public EventHandler ()
{
...
if (event_this_is_the_user_clicking){
// Send the message to the Delegate
Delegate.clicked ();
}
The user of the Button class then needs to implement the
interface ButtonDelegate to respond to messages. This in
Objective-C is made simple by allowing this interface to have
optional methods.
This would not work with C# interfaces as those require
that all the methods on the interface are implemented.
So we came up with a design that would blend those worlds
together.
Supporting the Objective-C approach
We can support the Objective-C model where a class can
satisfy the contract by just registering methods to respond to
the actions. A user could respond to some events like this:
class Foo : NSObject {
void ConfigureButton ()
{
button = new Button ("pause");
button.WeakDelegate = this;
}
[Export ("clicked")]
void pause_clicked ()
{
// Pause the button.
}
}
In MonoTouch we expose the "WeakDelegate" property as the
weakly-typed version of the delegate, that means that you can
assign to it anything that derives from the NSObject class,
and respond to messages by just annotating your method with
the [Export ("name")] attribute.
But weakly typing could introduce errors. What if the
parameters of the method or the return value do not match the
actual signature of the selector. If the developer introduce
a typo, the compiler would not be able to point this out, and
you would get a nice crash or some corrupt memory.
So we introduced strongly typed delegates, these require
the programmer to implement a class and override methods that
have a very specific signature. The code would then look
like this:
class Foo : ButtonDelegate {
void ConfigureButton ()
{
button = new Button ("pause");
button.Delegate = this;
}
// strongly typed: override ensures that this method exists in
// the base class, or the contract is not satisfied.
public override void pause_clicked ()
{
// Pause the button.
}
}
The ButtonDelegate class is flagged with a special
attribute, the [Model] attribute which informs the MonoTouch
runtime that only methods that are overwritten from this class
will be dispatched, any other methods will be considered
optional and treated like optional methods.
The problem of course is that if you wanted to respond to
multiple buttons, you would have to actually distinguish them
somewhere in your pause_clicked with some sort of closure
value, or use helper classes, one for each button that you
want to respond to. But that is a minor detail.
So Objective-C developers that like the separation between
model, view and controller can continue to use those
patterns.
That being said, although there is a lot of literature and
discussion about the clean separation of Models, Views and
Controllers, most samples I run across are a hodgepodge of
Controller code for all sorts of things. At least the
production code and the samples I have seen make it obvious
that the separation of concerns is mostly academic, and in
practice an instance object becomes a general purpose
responder to all events, very much like you see in Gtk+, Qt,
or Winforms.
Our approach also has a limitation: the hodgepodge approach
does not really work with the strongly-typed signatures as you
can only derive from one class at a time. The solution is to
use separate classes for each controller. Although that
cleans things up and has some aesthetics associated with it in
terms of clean separation of concerns, in my opinion, it is
not very practical.
Supporting the C# style
With MonoTouch we wanted to preserve the C# style of
programming where you could attach code to these notifications
one-by-one.
With C# you can use anonymous methods or lambda functions.
These are incredibly powerful as they are able to capture the
environment in which they were created. This means that you
can trivially pass information and share information between
multiple handlers.
Additionally, you more than one method can "subscribe" to
the same event, you are not limited to a single method being
the receiving of the events.
This means that in MonoTouch you configure buttons like
this:
void configure_button ()
{
button = new UIButton (dims) { Text = "Pause" };
button.Clicked += delegate {
// Pause the video
};
}
What MonoTouch does internally is that the first time that
you attach to a C#-like event, it create an instance of the
ButtonDelegate that merely raises the event in the C# style.
This means that you only pay for the memory usage if you
use the feature. But when you do, you can subscribe to
individual events and you can have more than one listener
attached to the event.
The Pattern
Although we brought the pattern to most places in MonoTouch
where a delegate was used, it is notably missing from the
UITableView as we felt that the number of methods that had to
be overwritten were too big for the model to make sense.
In a few instances like UITableView, we suggest that
developers just use the strongly typed version of the delegate
classes and override the methods accordingly.
One of the things that I would like to see is a UITableView
derived class that can present the data and style the data
entirely based on properties discovered at runtime with
System.Reflection.
Another thing that I want to see is support for System.Data
data binding style versions of the UITableView and other UI
controls in a single page.
More details on the low-level implementation are available
on the API
design document on the web site.