编辑:添加更改是因为有些人认为我应对 Objective-C 的局限性负责。
简短的回答:你不能。Objective-C 没有 Ruby mixins 的等价物。
稍微简短的回答:Objective-C 确实有一些可以说是相同风格的东西:协议。协议(某些其他语言中的接口)是一种定义一组方法的方法,该类采用该协议致力于实现。但是,协议不提供实现。该限制阻止了将协议用作与 Ruby mixin 完全等效的协议。
更简短的回答:然而,Objective-C 运行时有一个公开的 API,让您可以使用该语言的动态特性。然后你会跳出语言,但你可以拥有带有默认实现的协议(也称为具体协议)。弗拉基米尔的回答显示了一种方法。在那一点上,在我看来,你可以得到 Ruby mixins。
但是,我不确定我是否会建议这样做。在大多数情况下,其他模式可以满足要求,而无需与运行时玩游戏。例如,您可以有一个实现混合方法的子对象(has-a而不是is-a)。使用运行时是可以的,但有两个缺点:
作为旁注,我在下面声明类别不能将状态(实例变量)添加到类。通过使用运行时 API,您也可以解除该限制。然而,这超出了这个答案的范围。
长答案:
两个 Objective-C 特性看起来像是可能的候选者:类别和协议。如果我正确理解了这个问题,那么类别在这里并不是真正的正确选择。正确的功能是协议。
让我举个例子吧。假设你想让你的一些班级拥有一种叫做“唱歌”的特殊能力。然后定义一个协议:
@protocol Singer
- (void) sing;
@end
现在您可以通过以下方式声明您自己的任何类都采用该协议:
@interface Rectangle : Shape <Singer> {
<snip>
@end
@interface Car : Vehicle <Singer> {
<snip>
@end
通过声明他们采用协议,他们承诺实现该sing
方法。例如:
@implementation Rectangle
- (void) sing {
[self flashInBrightColors];
}
@end
@implementation Car
- (void) sing {
[self honk];
}
@end
然后,您可以像这样使用这些类:
void choral(NSArray *choir) // the choir holds any kind of singer
{
id<Singer> aSinger;
for (aSinger in choir) {
[aSinger sing];
}
}
请注意,数组中的歌手不需要有一个共同的超类。还要注意,一个类只能有一个超类,但可以有许多采用的协议。最后请注意,类型检查是由编译器完成的。
实际上,协议机制是用于混合模式的多重继承。多重继承受到严重限制,因为协议不能向类添加新的实例变量。协议仅描述采用者必须实现的公共接口。与 Ruby 模块不同,它不包含实现。
这是最重要的。然而,让我们提到类别。
类别不是在尖括号中,而是在括号之间声明。不同之处在于,可以为现有类定义一个类别来扩展它,而无需对其进行子类化。您甚至可以为系统类这样做。可以想象,可以使用类别来实现类似于 mixin 的东西。并且它们以这种方式使用了很长时间,通常作为类别NSObject
(继承层次结构的典型根),以至于它们被称为“非正式”协议。
这是非正式的,因为 1- 编译器不进行类型检查,2- 实现协议方法是可选的。
今天没有必要使用类别作为协议,特别是因为正式协议现在可以声明它们的一些方法是可选的,关键字是可选的,@optional
或者是必需的(默认)@required
。
类别对于向现有类添加一些特定于域的行为仍然很有用。NSString
是一个共同的目标。
有趣的是,大多数(如果不是全部)NSObject
设施实际上是在NSObject
协议中声明的。这意味着将其NSObject
用作所有类的公共超类并不是很有吸引力,尽管出于历史原因仍然通常这样做,而且......因为这样做没有缺点。但是某些系统类,例如NSProxy
,不是 NSObject
。