Objective-C支持traits/mixins吗?

12

有没有在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,所有控制器都从它派生出来,但如果我可以分解它,并让特定的控制器继承特定的行为,那就更好了。


能解释一下 traits 和 mixins 是什么吗?这些概念可能会有不同的名称来支持它们。 - Sherm Pendley
看起来有人在这里实现了Obj-C特性:http://etoileos.com//news/archive/2011/07/12/1427/ - Bill
3个回答

18

虽然没有直接的语言支持,但你可以通过消息转发实现类似的功能。比如你有"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的实例,并添加任何您想要的“trait”对象:

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

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

然后,您可以调用-doFoo和-doBar,就好像它们是由widget实现的一样,尽管消息被转发到其中一个特性对象:

[widget doFoo];
[widget doBar];

(编辑:添加了错误处理。)


太棒了!当你看到这个问题时,你不知道 traits 是什么,于是你进行了搜索,甚至实现了它!我想知道一件事,为什么需要重写 methodSignatureForSelector。难道 forwardInvocation 不够吗?methodSignatureForSelector 什么时候被调用?流程是什么? - Amogh Talpallikar

0

Objective-C不支持Traits或Mixins,你只有内置的分类选项。

但幸运的是,Objective-C Runtime几乎拥有所有工具来实现自己的混合或特性想法,通过在运行时向类添加方法和属性。您可以在苹果文档网站上阅读更多关于Objective-C Runtime为您提供的机会Objective-C Runtime Docs

思路如下:

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

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

3)使您希望提供与mixins组合的可能性的某些类符合该协议(Mixin)。

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

5)完成 :)

或者您可以使用一些开源项目,例如“Alchemiq


-4

1
不幸的是,我认为不行。我已经考虑过了,如果有某种方法可以实现它,那么它将涉及到类别。但是,类别只允许您将方法混合到特定类中,而不是多个类(对吧?),因此我认为它们不足够。但是我可能是错的。 - Bill
类别确实是在单个类上定义的。但是,从那个类继承的其他类也会继承这些类别。 - Zr40
4
在Scala的概念中,类别(categories)绝对不是特征(traits)。它们更像是C#中的部分类(partial classes)。 - Marc W

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接