id's. There has been some misunderstanding that I am opposed to types. I am not, but more on that another time.
One of the many nice properties of the (transitive) id subset is that it
is dynamically (memory) safe, just like Smalltalk. That is, as long as all arguments and return values
of your message are objects, you can never dereference a pointer incorrectly,
the worst that can happen is that you get a "Message not understood" that can
be caught and handled by the object in question or raised as an exception.
The reason this is safe is that
objc_msgSend() will make sure that methods
will only ever be invoked on objects of the correct class, no matter what the
(possibly incorrect, or unavailable) static type says.
So no de-referencing an incorrect pointer, no scribbling over random bits of memory. In fact, this is the vaunted "pointer safety" that John Siracusa says requires ditching native compiled languages like Objective-C for VM based languages. The idea that a VM with an interpreter or a JIT was required for pointer safety was never true, of course, and it's interesting that both Google and Microsoft are turning to Ahead of Time (AOT) compilation in their newest SDKs, for performance reasons.
Did someone mention "performance"? :-)
Another nice aspect of the id subset is that it makes reflective code
a lot simpler. And simplicity usually also translates to speed. How
much speed? Apple's
NSInvocation class has to deal with
interpreting C type information at runtime to then construct proper stack
frames dynamically for all possible C types. I think it uses libffi, though
it may be some equivalent library. This is slow, around 340.1ns
per message send on my 13" MBPR. By restricting itself to the id subset,
MPWFastInvocation class's dispatch is
much simpler, just a switch invoking
a different number of arguments.
The simplicity of
MPWFastInvocation also pays off in
speed: 6.2ns per message-send on the same machine. That's 50 times
NSInvocation and only 2-3x slower than
a normal message send. In fact, once you're that close, things like
IMP-caching (4 ns) start to make sense, especially since they can
be hidden behind a nice interface. Using a C Macro and the IMP
stashed in a public instance var takes the time down to 3 ns, making
the reflective call via an object effectively as fast as the
non-reflective code emitted by the compiler. Which is nice, because
it makes reflective techniques much more feasible for wider varieties
of code, which would be a good thing.
The speed improvement is not because MPWFastInvocation is better than NSInvocation, it is decidedly not, it is because it is solving a much, much simpler problem. By sticking to the safe id subset.