【问题标题】:Does Objective-C support traits/mixins?Objective-C 是否支持特征/混合?
【发布时间】:2011-04-08 21:49:18
【问题描述】:

在 Objective-C 中是否有任何技术可以模拟特征或 mixin?

例如,在 Scala 中,我可以这样做:

trait ControllerWithData {
  def loadData = ...
  def reloadData = ...
  def elementAtIndex = ...
}

trait ControllerWithStandardToolbar {
  def buildToolbar = ...
  def showToolbar = ...
  def hideToolbar = ...
}

class MyTableController extends ControllerWithData 
                        with ControllerWithStandardToolbar {
  def loadView = {
     super.loadView

     loadData
     buildBar
  }
}

它基本上是一种将多个功能组合(或混合)到一个类中的方法。所以现在我有一种通用的 UIViewController,我的所有控制器都从它继承,但是如果我可以分解它并让特定的控制器继承特定的行为会更整洁。

【问题讨论】:

标签: objective-c traits mixins


【解决方案1】:

没有直接的语言支持,但您可以通过消息转发完成类似的操作。假设您有特征类“Foo”和“Bar”,它们分别定义了方法“-doFoo”和“-doBar”。您可以将您的类定义为具有特征,如下所示:

@interface MyClassWithTraits : NSObject {
    NSMutableArray *traits;
}
@property (retain) NSMutableArray* traits;

-(void) addTrait:(NSObject*)traitObject;
@end

@implementation MyClassWithTraits
@synthesize traits;

-(id)init {
    if (self = [super init]) {
        self.traits = [NSMutableArray array];
    }
    return self;
}

-(void) addTrait:(NSObject*)traitObject {
    [self.traits addObject:traitObject];
}

/*  Here's the meat - we can use message forwarding to re-send any messages
    that are unknown to MyClassWithTraits, if one of its trait objects does
    respond to it.
*/
-(NSMethodSignature*)methodSignatureForSelector:(SEL)aSelector {
    // If this is a selector we handle ourself, let super handle this
    if ([self respondsToSelector:aSelector])
        return [super methodSignatureForSelector:aSelector];

    // Look for a trait that handles it
    else
        for (NSObject *trait in self.traits)
            if ([trait respondsToSelector:aSelector])
                return [trait methodSignatureForSelector:aSelector];

    // Nothing was found
    return nil;
}

-(void) forwardInvocation:(NSInvocation*)anInvocation {
    for (NSObject *trait in self.traits) {
        if ([trait respondsToSelector:[anInvocation selector]]) {
            [anInvocation invokeWithTarget:trait];
            return;
        }
    }

    // Nothing was found, so throw an exception
    [self doesNotRecognizeSelector:[anInvocation selector]];
}
@end

现在,您可以创建 MyClassWithTraits 的实例,并添加您想要的任何“特征”对象:

MyClassWithTraits *widget = [[MyClassWithTraits alloc] init];
[widget addTrait:[[[Foo alloc] init] autorelease]];
[widget addTrait:[[[Bar alloc] init] autorelease]];

如果您希望该类的每个实例都具有相同类型的特征,您可以在 MyClassWithTraits 的 -init 方法中对 -addTrait: 进行这些调用。或者,您可以像我在这里所做的那样进行操作,这样您就可以为每个实例分配一组不同的特征。

然后您可以调用-doFoo-doBar,就好像它们是由小部件实现的一样,即使消息被转发到它的特征对象之一:

[widget doFoo];
[widget doBar];

编辑:添加错误处理。)

【讨论】:

  • 太棒了!当你看到这个问题时,你不知道什么是特征,你搜索甚至实现了这个!!!我想知道一件事,为什么 methodSignatureForSelector 需要被覆盖。 forwardInvocation 还不够吗?何时调用 methodSignatureForSelector ?流程是什么?
【解决方案2】:

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

【讨论】:

    【解决方案3】:

    【讨论】:

    • 不幸的是,我不这么认为。我考虑过这一点,我认为如果有办法做到这一点,它将涉及类别。但是类别允许您将方法混合到一个特定的类中,而不是多个类(对吗?),所以我认为它们还不够。但我可能是错的。
    • 类别确实是在单个类上定义的。但是,从该类继承的其他类也会继承这些类别。
    • 类别绝对不是 Scala 意义上的特征。它们更像是 C# 领域中的部分类。
    猜你喜欢
    • 2012-05-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-11-20
    • 2011-10-01
    相关资源
    最近更新 更多