Mixins или множественное наследование в Objective-C?
Скажем, например, что у меня есть MyUITextViewSubclass
который наследует от UITextView
а также MyUITextFieldSubclass
который наследует от UITextField
и оба этих подкласса содержат много одинаковых методов и свойств для добавления сходного поведения к этим элементам управления пользовательского интерфейса.
поскольку UITextView
а также UITextField
наследовать от разных классов, есть ли простой способ создать абстрактный класс, чтобы объединить весь этот повторяющийся код? Другими словами, возможно ли создать абстрактный класс, от которого я мог бы унаследовать оба этих подкласса, а затем просто переопределить методы, которые отличаются между ними?
Что я знаю до сих пор:
- Я знаю, что Objective-C не поддерживает множественное наследование (наследование от двух или более классов)
- Я знаю, что могу добавить общие методы, используя Categories, но я не думаю, что это решает переопределение методов init или добавление приватных свойств
5 ответов
Основываясь на ответе Амина, вот как вы могли бы это сделать:
Шаг 1. Создайте TextSurrogateHosting
протокол, который будет содержать все методы вашего UITextField
а также UITextView
подклассы, к которым вам нужно получить доступ из методов, которые вы хотите добавить в оба подкласса. Это может быть, например, text
а также setText:
метод, так что ваши методы могут получить доступ и установить текст либо текстового поля или текстового представления. Это может выглядеть так:
SPWKTextSurrogateHosting.h
#import <Foundation/Foundation.h>
@protocol SPWKTextSurrogateHosting <NSObject>
- (NSString *)text;
- (void)setText:(NSString *)text;
@end
Шаг 2: Создайте TextSurrogate
класс, который содержит все методы, которые вы хотите поделиться между UITextField
и UITextView
подклассы. Добавьте эти методы в протокол, чтобы мы могли использовать завершение кода в XCode и избежать предупреждений / ошибок компилятора.
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
Шаг 3: Создайте свой UITextField
подкласс. Он будет содержать три необходимых стандартных метода для пересылки нераспознанных вызовов методов на ваш 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
Шаг 4: Создайте свой UITextView
подкласс.
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
Шаг 5: Используйте это:
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
Этот шаблон должен решить вашу проблему. С надеждой:)
Дополнительная справочная информация доступна здесь: https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/ObjCRuntimeGuide/Articles/ocrtForwarding.html
То, что вы хотите, это миксин. Это не поддерживается в Objective-C. Категории не являются миксинами, потому что они добавляют API к одному классу, а не ко многим (>1) классам. Использование категорий, что, как вы сказали, невозможно по многим причинам, не поможет вам.
Обычный способ решить эту проблему - создать вспомогательный класс, содержащий дополнительный код, и использовать его в обоих классах.
Тогда вы обнаружите, что печатаете
[myUITextViewSubclass.helper doSomething]
вместо
[myUITextViewSubclass doSomething]
Если это действительно проблема, вы можете решить ее с помощью прямых вызовов. Просто напишите комментарий.
Вы можете использовать макрос препроцессора, но он подвержен ошибкам.
Нет, это невозможно.
Самое близкое, чего вы могли бы достичь - это вручную добавить функциональность в UITextView
чтобы подражать UITextField
, Очевидным недостатком является то, что вы должны делать все это вручную, с вашим собственным кодом.
Черты или миксины не поддерживаются Objective-C, у вас есть только встроенная опция категорий. Но, к счастью, в Objective-C Runtime есть почти все инструменты для реализации собственной идеи смешивания или черт с добавлением методов и свойств в ваш класс во время выполнения. Вы можете прочитать больше о возможностях, которые Objective-C Runtime предоставляет для вас на веб - сайте документации Apple, Objective-C Runtime Docs
Идея заключается в следующем:
1) Вы можете создать протокол Objective-C (Mixin), в котором вы будете объявлять свойства и методы.
2) Затем вы создаете класс (реализация Mixin), который будет реализовывать методы из этого протокола.
3) Вы делаете свой некоторый класс, в котором вы хотите предоставить возможность композиции с миксинами, чтобы соответствовать этому протоколу (Mixin).
4) Когда ваше приложение запускается, вы добавляете во время выполнения Objective C все реализации из класса (реализации Mixin) и свойства, объявленные в (Mixin), в ваш класс.
5) вуаля:)
Или вы можете использовать несколько готовых проектов с открытым исходным кодом, таких как " Alchemiq "