</br>
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
similar capabilities:
@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 @protocol
Logable
.
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.
In example:
Controller *controller;
UIView <Printable, Logable> *view = controller.view;
....
Potential alternatives might be to make Printable
descend from Logable
or
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
type, 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.