Polymorphism in Objective-C can be achieved with protocols. Protocols can be used to create abstract interfaces and hide classes. The traditional approach to implementing multiple protocols can result in hard to maintain code.
For example, here’s 2 subclasses of
UIView that share
@protocol Printable <NSObject> - (void)printToFile:(NSFileHandle *)file; @end @interface ViewA : UIView <Printable> @end @interface ViewB : UIView <Printable> @end
It’s straight forward to vend an abstract interface that could be backed by either of these subclasses:
@interface Controller : NSObject // This view could be type ViewA, ViewB, or any other // view that implements the Printable protocol. // The actual class is an implementation detail. @property (nonatomic) UIView <Printable> *view; @end
It’s straight forward to consume it as well:
Controller *controller; UIView <Printable> *view = controller.view; ....
This pattern breaks down if capabilities need to be added to the type. For example, if the
view needed to expose the additional
The interface declaration must be changed:
@interface Controller : NSObject @property (nonatomic) UIView <Printable, Logable> *view; @end
There’s downsides to this change: all existing uses need to change, it’s not nice to read, and it’s hard to type. It’s unmaintainable.
Controller *controller; UIView <Printable, Logable> *view = controller.view; ....
Potential alternatives might be to make
Printable descend from
even just add all the methods from
Logable. If you’re the vendor of
Printable, the change is certainly possible. But, existing implementors of
Printable, might not need to be extended with
Logable. This is asking for
strange semantics and a bloated protocol.
There’s an oldschool technique that can be used to simplify the interface. It’s called typdeffing and its widely used in Objective-C apps, just not for this purpose.
I call it Typedef’d polymorphism:
typedef UIView <Printable, Logable> View;
“Typdef View as a UIView that implements Printable and Logable.”
By declaring the new type, we introduce a clean layer of abstraction. The new
View, is a
UIView and implements both protocols.
It’s easier for humans to parse than
UIView <Printable, Logable>
*. It reduces the complexity to a single place. It’s maintainable.
It radically simplifies the interface:
@interface Controller : NSObject @property (nonatomic) View *view; @end
Controller *controller; View *view = controller.view; ....
The newly found type abstraction layer adds opportunity for (sinful) flexibility. For example:
The type can be directly implemented:
// Valid Objective-C @interface PrintableLogableView : View @end
The type can be conditionally defined:
// Valid Objective-C #ifdef DEBUG typedef UIView <Printable, Logable, DebugCapabilites> View; #else typedef UIView <Printable, Logable> View; #endif
The possibilities are endless!
I’ve never see this approach used in Apple frameworks i.e. this type of problem isn’t exposed in UIKit. I don’t think this approach is widely used at all.
Typdeffing is not only useful to define simple types: it’s a powerful technique that can be used to create flexible, maintainable, abstract interfaces. With great power comes great responsibility.
For more fun Objective-C trickery, check out Mike Ash’s recent Friday Q&A on Preprocessor Abuse and Optional Parentheses.