Mixins or Multiple Inheritance in Objective-C? Mixins or Multiple Inheritance in Objective-C? objective-c objective-c

Mixins or Multiple Inheritance in Objective-C?


Building on Amin's answer, this is how you could do it:

Step 1: Create a TextSurrogateHosting protocol that will contain all the methods of your UITextField and UITextView subclasses that you need to access from the methods that you want to add to both subclasses. This might for example be a text and setText: method, so that your methods can access and set the text of either a text field or a text view. It might look like this:

SPWKTextSurrogateHosting.h

#import <Foundation/Foundation.h>@protocol SPWKTextSurrogateHosting <NSObject>- (NSString *)text;- (void)setText:(NSString *)text;@end

Step 2: Create a TextSurrogate class that contains all the methods that you want to share between both the UITextField and the UITextView subclasses. Add those methods to a protocol so that we can use code completion in Xcode and avoid compiler warnings/errors.

SPWKTextSurrogate.h

#import <Foundation/Foundation.h>#import "SPWKTextSurrogateHosting.h"@protocol SPWKTextSurrogating <NSObject>@optional- (void)appendQuestionMark;- (void)appendWord:(NSString *)aWord;- (NSInteger)characterCount;- (void)capitalize;@end@interface SPWKTextSurrogate : NSObject <SPWKTextSurrogating>/* We need to init with a "host", either a UITextField or UITextView subclass */- (id)initWithHost:(id<SPWKTextSurrogateHosting>)aHost;@end

SPWKTextSurrogate.m

#import "SPWKTextSurrogate.h"@implementation SPWKTextSurrogate {    id<SPWKTextSurrogateHosting> _host;}- (id)initWithHost:(id<SPWKTextSurrogateHosting>)aHost{    self = [super init];    if (self) {        _host = aHost;    }    return self;}- (void)appendQuestionMark{    _host.text = [_host.text stringByAppendingString:@"?"];}- (void)appendWord:(NSString *)aWord{    _host.text = [NSString stringWithFormat:@"%@ %@", _host.text, aWord];}- (NSInteger)characterCount{    return [_host.text length];}- (void)capitalize{    _host.text = [_host.text capitalizedString];}@end

Step 3: Create your UITextField subclass. It will contain three necessary boilerplate methods to forward unrecognized method invocations to your SPWKTextSurrogate.

SPWKTextField.h

#import <UIKit/UIKit.h>#import "SPWKTextSurrogateHosting.h"#import "SPWKTextSurrogate.h"@interface SPWKTextField : UITextField <SPWKTextSurrogateHosting, SPWKTextSurrogating>@end

SPWKTextField.m

#import "SPWKTextField.h"@implementation SPWKTextField {    SPWKTextSurrogate *_surrogate;}- (id)initWithFrame:(CGRect)frame{    self = [super initWithFrame:frame];    if (self) {        _surrogate = [[SPWKTextSurrogate alloc] initWithHost:self];    }    return self;}#pragma mark Invocation Forwarding- (void)forwardInvocation:(NSInvocation *)anInvocation{    if ([_surrogate respondsToSelector:[anInvocation selector]]) {        [anInvocation invokeWithTarget:_surrogate];    } else {        [super forwardInvocation:anInvocation];    }}- (NSMethodSignature*)methodSignatureForSelector:(SEL)selector{    NSMethodSignature* signature = [super methodSignatureForSelector:selector];    if (!signature) {        signature = [_surrogate methodSignatureForSelector:selector];    }    return signature;}- (BOOL)respondsToSelector:(SEL)aSelector{    if ([super respondsToSelector:aSelector] ||        [_surrogate respondsToSelector:aSelector])    {        return YES;    }    return NO;}@end

Step 4: Create your UITextView subclass.

SPWKTextView.h

#import <UIKit/UIKit.h>#import "SPWKTextSurrogateHosting.h"#import "SPWKTextSurrogate.h"@interface SPWKTextView : UITextView <SPWKTextSurrogateHosting, SPWKTextSurrogating>@end

SPWKTextView.m

#import "SPWKTextView.h"#import "SPWKTextSurrogate.h"@implementation SPWKTextView {    SPWKTextSurrogate *_surrogate;}- (id)initWithFrame:(CGRect)frame{    self = [super initWithFrame:frame];    if (self) {        _surrogate = [[SPWKTextSurrogate alloc] initWithHost:self];    }    return self;}#pragma mark Invocation Forwarding- (void)forwardInvocation:(NSInvocation *)anInvocation{    if ([_surrogate respondsToSelector:[anInvocation selector]]) {        [anInvocation invokeWithTarget:_surrogate];    } else {        [super forwardInvocation:anInvocation];    }}- (NSMethodSignature*)methodSignatureForSelector:(SEL)selector{    NSMethodSignature* signature = [super methodSignatureForSelector:selector];    if (!signature) {        signature = [_surrogate methodSignatureForSelector:selector];    }    return signature;}- (BOOL)respondsToSelector:(SEL)aSelector{    if ([super respondsToSelector:aSelector] ||        [_surrogate respondsToSelector:aSelector])    {        return YES;    }    return NO;}@end

Step 5: Use it:

SPWKTextField *textField = [[SPWKTextField alloc] initWithFrame:CGRectZero];SPWKTextView *textView = [[SPWKTextView alloc] initWithFrame:CGRectZero];textField.text = @"The green fields";textView.text = @"What a wonderful view";[textField capitalize];[textField appendWord:@"are"];[textField appendWord:@"green"];[textField appendQuestionMark];NSLog(@"textField.text: %@", textField.text);// Output: The Green Fields are green?[textView capitalize];[textView appendWord:@"this"];[textView appendWord:@"is"];NSLog(@"textView.text: %@", textView.text);// Output: What A Wonderful View this is

This pattern should solve your problem. Hopefully :)

Some more background information is available here: https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/ObjCRuntimeGuide/Articles/ocrtForwarding.html#//apple_ref/doc/uid/TP40008048-CH105


What you want is a mixin. This is not supported in Objective-C. Categories are no mixins, because they add an api to one class not to many (>1) classes. Using categories, what is not possible for many reasons as you said, would not help you.

The usual way to solve that problem is to create a helper class containing the additional code and use it in both classes.

Then you will find yourself typing

[myUITextViewSubclass.helper doSomething]

instead of

[myUITextViewSubclass doSomething]

If this is really a problem, you can solve this with forward invocations. Just write a comment.


No. It is not possible.

The closest thing you could achieve would be to manually add functionality to UITextView to make it mimic UITextField. The obvious downside is that you must do this all manually, with your own code.