I dislike prefixing class names. It seems like a hack that should have a better solution. That said, though, the practice works well enough so I got to thinking about a possible "namespaces-like" hack that might make the situation more pleasant while making use of the current conventions. Can a hack fix a hack? Let's find out!
The idea is to add a compiler directive somewhat similar to @compatibility_alias, but with more complicated rules. For now I'll just call it @class_prefix.
The first example would be using it to define a new class:
@interface String : NSObject
So, when this is compiled it makes a class named NSString simply by appending class name defined by @interface to the @class_prefix.
If there is no @class_prefix defined in the same (header/scope/context/complication-unit?) the name of the class would work just like it always has. Example:
@interace String : NSObject
Creates a class named String. As expected.
Now that alone doesn't really add much. The next thing is being able to use these without having to use the prefixes when you use the classes, if possible.
I'll try to explain how I imagine this working. Within a single file (header and implementation would be treated independently), there'd be a concept of a current "defining prefix". This prefix is the prefix that's used when generating the real name of the class whenever an @interface is encountered. It is always nil/none until a @class_prefix is encountered directly in the file itself. This means that even if you import headers that have @class_prefix in them, the defining prefix is still nil. @class_prefix must exist within the file itself (or I suppose by way of #include) before the defining prefix changes.
While defining prefix requires @class_prefix to exist within the file directly, looking up real class names does not work that way. Instead, as files are #imported, a stack of class_prefixes builds up which defines a lookup-order for resolving class names. When a class is referenced, it would first append the top prefix on the stack to the given name, see if that exists, and if so, use it. If not, it pops the stack and tries again. Eventually the stack runs out and it tries the bare name given in the source code. If that also fails, it finally generates a compiler error about not being able to find a class with the given name.
So, with those rules in mind, here's a silly example scenario:
@interface View : Object
@interface AudioPlayer : Object
@interface Test : Object
View *myView = [[View alloc] initWithFrame:…];
AudioPlayer *player = [AudioPlayer …];
In NSObject.h, we are not importing anything so the class_prefix stack is empty. When it encounters @class_prefix NS, the class_prefix stack is set to "NS" and the defining prefix is set to "NS". After we define our class named "Object" it uses the defining prefix to *actually* name the class NSObject.
In UIView.h, we start out with an empty class_prefix stack until we import NSObject.h. After that, the stack is "NS" and the defining prefix is nil. We then encounter @class_prefix UI, so the stack is now "NS, UI" with the defining prefix set to "UI". As we define the "View" class, it has a super class of "Object" which we need to look up. So the first thing we do is try finding a class named UIObject because the prefix_stack is "NS, UI". Since that fails, we then pop that stack and try again with "NSObject". This succeeds so we can move on to defining the rest of the View class. When the View class is actually created, it is named UIView because the defining prefix is "UI".
The AVAudioPlayer.h example works the same way as the UIView example.
When Test.m is processed, we first start off with an empty class_prefix stack and a nil defining prefix as usual. After importing UIView.h, we have a class_prefix stack of "NS, UI" and a defining prefix of nil. We them import AVAudioPlayer.h and end up with a class_prefix stack of "NS, UI, NS, AV" and a defining prefix of nil. After that, we encounter a class definition for a class named "Test". Since the defining prefix is still nil, the actual name of this class is exactly as it appears: "Test". When looking up the Object class that is specified as the superclass of Test, we proceed down the class_prefix stack first checking for AVObject, then NSObject. The same name lookups apply when compiling the -testMethod.
So anyway, that's the idea. A way to get rid of prefixes while still having prefixes! Woo!
A fun side effect of this approach is that existing classes with prefixes would already work with it as long as the @prefix_class directives were set up ahead of time somehow. It might be desirable to have an alternative directive for redefining the class_prefix stack within a file without changing the defining prefix:
@class_prefix_order NS, UI
That's a terrible name and I'm sure something better could be used, but that'd have the effect of overriding the implied class prefix search order by basically defining the class_prefix stack *for that file* but not messing with the defining prefix. So using a directive like the above, you could import the usual NS and UI classes from the standard frameworks but within your source file, you could reference them without their classic prefixes by using the above directive and it'd work the same while looking and reading cleaner. And since you may not have defined your own defining prefix by way of @class_prefix directly, your own classes defined in that file would still be added like normal using the exact name specified in your @interface. (Unless of course you set @class_prefix to something yourself within that file.)