Ancient history. NeXT came out with a technology called "Distributed Objects", or DO for short. The current implementation exists today inside their Foundation framework. The original implementation was not even based on Foundation and was not implemented as cleanly as the current incarnation, it had some problems which Foundation fixed.
Some programmers decided they wanted their own version of this original DO and it would sit on top of the GNU runtime. Without a clear vision of how DO worked, in part due to the lack of documentation and more open interface from NeXT, they would implement DO in a way which they thought was right. What they thought was Right would ask of the GNU runtime to do things which it could not possibly do reliably.
I won't beat around the bush on this. Typed selectors were a Big Mistake. They should be phased out of use.
To understand how typed selectors came about, one needs to understand a major idea behind DO. In Objective-C when an object does not respond to a message, the runtime does what is called "forwarding". It invokes a known/predefined method which takes the selector, the arguments from the stack/registers and asks the object what it wants to do with them. The default implementation is typically to error out, raise an exception, quit, up to the runtime.
DO came up with this fantastic idea to have an almost implementation-less object called a "proxy", which takes these forwards and sends them over the network to the implementation on a remote host. Instead of code generation and stubs, they use the runtime type information to encode the arguments.
The problem is that when you are given a forward all you have is a name and the arguments. No type information. How to implement this became more obvious with the release of OpenStep, but typed selectors predate OpenStep and a lot of people outside of NeXT were not as clear on how things should be done.
The way it works today is that before a proxy is asked to forward a message, the type information for the message is requested. The proxy asks the remote implementation how the types are encoded and uses this information to encode the arguments of the forward for transmit and how to decode the return value.
The guys trying to write DO on their own either didn't know this or thought this was not a good implementation. Why ask the remote system when you can have the compiler encode the type information locally, in the selector! This seems like a good idea on the surface but it breaks a cardinal rule of Objective-C.
Selectors are supposed to be uniqueOn the surface this doesn't seem like a big deal, but it has this evil little side effect. You can have multiple selectors with the same name
and different types. If you need to get the types when all you have is the name, the results are
not reliably defined, because there can be multiple typed selectors with the same name and different types.
How to fix typed selectorsGet rid of them. Implement them the way they are supposed to be, a name<->unique id mapping. Typically the unique id is the name, but it doesn't have to be.
Ack, but getting rid of things is not so easy in software, someone somewhere is probably using them. They are used in, of course, GNUStep, the inheritor of the old free DO implementation. Let's take a look at how GNUStep is using typed selectors.
There are two functions we are interested in. sel_get_type, this takes the name of a selector and returns the types associated with it, if they are present. It is possible to have a "typed selector" with no types! The other function of interest is sel_get_any_uid.
sel_get_any_uid should have been the little guy on the programmers shoulder going "Dude, this looks really wrong". sel_get_any_uid takes a name and returns *ANY* typed selector with that name. If there are two selectors with different types, it will return one of them. So just sit and pray it returns the right one, and somewhere out there in the sea of selectors, there isn't another method with the same name and different types.
Let's talk about sel_get_type, since if you are using sel_get_type you may be using sel_get_any_uid. To get rid of sel_get_type means you can get rid of sel_get_any_uid.
sel_get_type is only used in GNUStep base, their Foundation work-alike. I pulled down gnustep-base-1.11.2 and looked for sel_get_type. It shows up in these files:
mframe.m, cifframe.m, callframe.m
GSFFIInvocation.m
GSFFCallInvocation.m
NSDistantObject.m
NSConnection.m
NSInvocation.m
---
It turns out that mframe, cifframe and callframe are copy&paste versions of the same code with variations. Their use of sel_get_type is the same. The interesting thing about them is that they also implement their functionality in terms of the NeXT runtime interface. So, sel_get_type isn't actually needed here. It exists, someone used it. It's use can be easily removed.
---
GSFFIInvocation.m and GSFFCallInvocation are also copy&paste versions of the same code with variations. From GSFFInvocation.m:
/* Determine the method types so we can construct the frame. We may not
get the right one, though. What to do then? Perhaps it can be fixed up
in the callback, but only under limited circumstances.
*/
sel_type = sel_get_type (sel);
This is a perfect example of where a comment is appropriate. It flags the use of sel_get_type as broken and a better way to do things is needed. I rest my case on GSFFIInvocation and GSFFCallInvocation.
---
The people working on NSDistantObject know what they're doing. They have deprecated the use of sel_get_type in favor of the right way to do it. I don't need to say anything here. But why not get rid of this code altogether?
/**
* For backward compatibility ... do not use this method.
* Returns the type information ... the modern way of doing this is
* with the -methodSignatureForSelector: method.
*/
- (const char *) selectorTypeForProxy: (SEL)selector
{
#if NeXT_RUNTIME
/* This isn't what we want, unless the remote machine has
the same architecture as us. */
const char *t;
t = [_connection typeForSelector: selector remoteTarget: _handle];
return t;
#else /* NeXT_runtime */
return sel_get_type (selector);
#endif
}
---
NSConnection has an interesting use of sel_get_type
/* get the method types from the selector */
#if NeXT_RUNTIME
[NSException
raise: NSGenericException
format: @"Sorry, distributed objects does not work with NeXT runtime"];
/* type = [object selectorTypeForProxy: sel]; */
#else
type = sel_get_type(sel);
if (type == 0 || *type == '\0')
{
type = [[object methodSignatureForSelector: sel] methodType];
if (type)
{
sel_register_typed_name(GSNameFromSelector(sel), type);
}
}
#endif
It attempts to look up the types of the selector, if they are not found, it asks the proxy for the types, it then maps the result to a name. So, in effect it is doing the right thing by using methodSignatureForSelector, but all it does with typed selectors is use them as a global cache for name to type mapping. How this should be done is to always call methodSignatureForSelector: and leave it up to the proxy to implement a cache. A cache which properly maps names and types localized for the proxy's remote implementation, not the entire runtime. If the proxy did this, the NeXT runtime version would work fine too.
---
NSInvocation.m
This is the last use of sel_get_type, so far all the other uses have been hugely flawed, let's see what is happened here.
- (id) initWithSelector: (SEL)aSelector
{
const char *types;
NSMethodSignature *newSig;
types = sel_get_type(aSelector);
if (types == 0)
{
types = sel_get_type(sel_get_any_typed_uid(GSNameFromSelector(aSelector)));
}
if (types == 0)
{
NSLog(@"Couldn't find encoding type for selector %s.",
GSNameFromSelector(aSelector));
RELEASE(self);
return nil;
}
newSig = [NSMethodSignature signatureWithObjCTypes: types];
return [self initWithMethodSignature: newSig];
}
Let's examine the second use first:
types = sel_get_type(sel_get_any_typed_uid(GSNameFromSelector(aSelector)));
Because sel_get_any_typed_uid's result can be not reliable, the use of sel_get_type with the return value is flawed. This needs to be another way. But let's look at the first use before we do that.
The first use looks very innocuous, but wait, what is initWithSelector: doing in NSInvocation to begin with. An NSInvocation should only come to exist with a NSMethodSignature. initWithSelector: is bypassing NSMethodSignature in favor of using typed selectors. Use NSMethodSignature's and initWithSelector: goes away.
The GNUStep guys know what their doing, the problem is that they are afraid to let go of the old code. Understandable, but let it go man, just let it go.
If you are absolutely desperate for them, create gcc -ftyped-selectors and maintain your own broken runtime which supports them.
Smackdown results
-fnext-runtime: 1
-fgnu-runtime: 0