【问题标题】:Mixins or Multiple Inheritance in Objective-C?Objective-C 中的混合或多重继承?
【发布时间】:2017-01-31 04:29:56
【问题描述】:

假设我有 MyUITextViewSubclass 继承自 UITextViewMyUITextFieldSubclass 继承自 UITextField 并且这两个子类都包含许多相同的方法和属性以向这些 UI 添加类似的行为控制。

既然UITextViewUITextField 继承自不同的类,有没有一种简单的方法可以创建一个抽象类来组合所有重复的代码? 换句话说,是否可以为这两个子类创建一个我可以继承的抽象类,然后只覆盖两者之间不同的方法?

到目前为止我所知道的:

  • 我知道 Objective-C 不支持多重继承(从两个或多个类继承)
  • 我知道我可以使用类别添加常用方法,但我认为这不能解决重写 init 方法或添加私有属性的问题

【问题讨论】:

  • 您能否更具体地说明常见行为是什么?

标签: ios objective-c uitextfield uitextview abstract-class


【解决方案1】:

基于 Amin 的回答,您可以这样做:

步骤 1: 创建一个 TextSurrogateHosting 协议,该协议将包含您的 UITextFieldUITextView 子类的所有方法,您需要从要添加到两者的方法中访问这些方法子类。例如,这可能是 textsetText: 方法,以便您的方法可以访问和设置文本字段或文本视图的文本。它可能看起来像这样:

SPWKTextSurrogateHosting.h

#import <Foundation/Foundation.h>

@protocol SPWKTextSurrogateHosting <NSObject>

- (NSString *)text;
- (void)setText:(NSString *)text;

@end

第 2 步:创建一个 TextSurrogate 类,其中包含您希望在 UITextFieldUITextView 子类之间共享的所有方法。将这些方法添加到协议中,以便我们可以在 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#//apple_ref/doc/uid/TP40008048-CH105

【讨论】:

  • 绝对是一个有趣的方法!这解决了我原来方法的大部分问题。感谢分享:)
【解决方案2】:

你想要的是一个mixin。这在 Objective-C 中不受支持。类别不是 mixins,因为它们将 api 添加到 one 类而不是 many (>1) 类。使用类别,如您所说,由于多种原因而无法实现的功能对您没有帮助。

解决这个问题的常用方法是创建一个包含附加代码的帮助类,并在两个类中使用它。

然后你会发现自己在打字

[myUITextViewSubclass.helper doSomething]

而不是

[myUITextViewSubclass doSomething]

如果这确实是一个问题,您可以通过前向调用来解决这个问题。写评论就好了。

【讨论】:

    【解决方案3】:

    没有。这是不可能的。

    您可以实现的最接近的事情是手动向UITextView 添加功能以使其模仿UITextField。明显的缺点是您必须使用自己的代码手动完成所有这些操作。

    【讨论】:

      【解决方案4】:

      您可以使用预处理器宏,但这很容易出错。

      【讨论】:

        【解决方案5】:

        Objective-C 不支持 Traits 或 Mixins,您只有内置的 Categories 选项。 但幸运的是,Objective-C Runtime 拥有几乎所有工具来实现自己的想法,如果在运行时将方法和属性添加到类中,则可以混合或使用特征。您可以在 Apple 的文档网站 Objective-C Runtime Docs 上阅读有关 Objective-C 运行时为您提供的机会的更多信息

        想法是:

        1) 您可以创建一个 Objective-C 协议 (Mixin),您将在其中声明属性和方法。

        2) 然后创建一个类(Mixin 实现),它将实现该协议中的方法。

        3) 您创建了一些类,您希望在其中提供与 mixin 组合的可能性,以符合该协议 (Mixin)。

        4) 当您的应用程序启动时,您使用 Objective-C 运行时将 (Mixin 实现) 类中的所有实现和 (Mixin) 中声明的属性添加到您的类中。

        5) 瞧 :)

        或者你可以使用一些现成的开源项目,例如“Alchemiq

        【讨论】:

          猜你喜欢
          • 2018-01-31
          • 2021-04-02
          • 2011-05-10
          • 1970-01-01
          • 2014-04-30
          • 2011-05-28
          • 2011-08-22
          • 1970-01-01
          相关资源
          最近更新 更多