Objective-C中的Mixins或Multiple Inheritance
比方说,我有MyUITextViewSubclass
inheritance自UITextView
和MyUITextFieldSubclass
从UITextField
inheritance,这两个子类包含很多相同的方法和属性添加到这些UI控件的相似的行为。
由于UITextView
和UITextField
从不同的类inheritance,是否有一种简单的方法来创build一个抽象类来组合所有重复的代码? 换句话说,是否有可能为这两个子类创build一个可以inheritance的抽象类,然后重写两者之间不同的方法?
到目前为止我所知道的是:
- 我知道Objective-C不支持多重inheritance(从两个或更多的类inheritance)
- 我知道我可以使用类别添加通用的方法,但我不认为这解决了重写init方法或添加私有属性
build立在阿敏的答案,这是你如何做到这一点:
第1步:创build一个TextSurrogateHosting
协议,该协议将包含您需要从要添加到两个子类中的方法访问的UITextField
和UITextView
子类的所有方法。 例如,这可能是一个text
和setText:
方法,以便您的方法可以访问和设置文本字段或文本视图的文本。 它可能看起来像这样:
SPWKTextSurrogateHosting.h
#import <Foundation/Foundation.h> @protocol SPWKTextSurrogateHosting <NSObject> - (NSString *)text; - (void)setText:(NSString *)text; @end
第2步:创build一个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步:创build您的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步:创build你的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
这种模式应该能解决你的问题。 希望 :)
更多的背景信息可以在这里find: https : //developer.apple.com/library/mac/documentation/Cocoa/Conceptual/ObjCRuntimeGuide/Articles/ocrtForwarding.html#//apple_ref/doc/uid/TP40008048-CH105
你想要的是一个混合。 Objective-C不支持这个function。 分类是没有混入,因为他们添加到一个类的api不是很多 (> 1)类。 使用类别,如你所说,有许多原因是不可能的,不会帮助你。
解决这个问题的常用方法是创build一个包含附加代码的助手类,并在两个类中使用它。
然后你会发现自己打字
[myUITextViewSubclass.helper doSomething]
代替
[myUITextViewSubclass doSomething]
如果这真的是一个问题,你可以用forward调用来解决这个问题。 只要写评论。
不,这是不可能的。
您可以实现的最接近的function是手动添加function到UITextView
使其模仿UITextField
。 显而易见的缺点是,你必须手动完成这一切,用自己的代码。
你可以使用预处理macros,但是这是很容易出错的。
Objective-C不支持Traits或Mixins,只有内置的类别选项。 幸运的是,Objective-C Runtime几乎包含了所有用于实现自己的想法的工具,如果混合或者在运行时为你的类添加方法和属性。 您可以在Apple的文档网站Objective-C Runtime Docs上阅读更多关于Objective-C Runtime为您提供的机会
这个想法是:
1)您可以创build一个Objective-C协议(Mixin),在其中您将声明属性和方法。
2)然后你创build一个类(Mixin实现),它将实现这个协议的方法。
3)你让你想要提供与mixin组合的可能性的类,以符合该协议(Mixin)。
4)当你的应用程序启动时,你将(Mixin实现)类和(Mixin)中声明的属性的所有实现添加到Objective-C运行时到你的类中。
5)voilà:)
或者你可以使用一些现成的开源项目,比如“ Alchemiq ”