Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Mixins or Multiple Inheritance in Objective-C?

Let's say for example that I have MyUITextViewSubclass which inherits from UITextView and MyUITextFieldSubclass which inherits from UITextField and both of those subclasses contain a lot of the same methods and properties to add similar behavior to those UI controls.

Since UITextView and UITextField inherit from different classes, is there an easy way to create an abstract class to combine all of that repeated code? In other words, is it possible to create an abstract class that I could inherit from for both of those subclasses and then just override the methods that are different between the two?

What I know so far:

  • I know Objective-C doesn't support multiple inheritance (inheritance from two or more classes)
  • I know I could add common methods using Categories, but I don't think that solves overriding init methods or adding private properties
like image 653
ZeNewb Avatar asked Oct 22 '13 00:10

ZeNewb


People also ask

Should I use Mixins or inheritance?

A mixin is typically used with multiple inheritance. So, in that sense, there's "no difference". The detail is that a mixin is rarely useful as a standalone object.

Does Objective C support multiple inheritance?

In keeping with its clean and simple design, Objective C does not support multiple inheritance, though features have been developed to replace some of the functionality provided by multiple inheritance (see run-time section below). The root of the Objective C class hierarchy is the Object class.

Are Mixins composition or inheritance?

Mixins are a form of object composition, where component features get mixed into a composite object so that properties of each mixin become properties of the composite object.

What is the difference between a mixin and inheritance C#?

That means, Mixin can be referred to as “included” instead of “inherited”. Mixin brings in the code reusability and can be used to avoid the classic diamond of death problem because of inheritance ambiguity that can be caused by multiple inheritance.


1 Answers

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

like image 90
Johannes Fahrenkrug Avatar answered Nov 15 '22 14:11

Johannes Fahrenkrug