Objective-C是否像Ruby一样支持Mixin?

时间:2010-03-21 07:21:38

标签: ruby mixins objective-c-2.0

在Ruby中,有模块,您可以通过“混入”模块来扩展类。

module MyModule
  def printone
    print "one" 
  end
end

class MyClass
  include MyModule
end

theOne = MyClass.new
theOne.printone 
>> one

在Objective-C中,我发现我有一组常用的方法,我想要一些Class来“继承”。如果不创建一个公共类并从该公共类派生所有其他方法,我可以通过什么方式实现这一目标?

4 个答案:

答案 0 :(得分:31)

无耻的插件:ObjectiveMixin

它利用了Objective-C运行时在运行时向类添加方法的功能(与类别相反,只是编译时)。看看它,它的效果非常好,并且与Ruby的mixins类似。

答案 1 :(得分:28)

修改:添加了更改,因为有些人认为我对Objective-C的限制负责。

简短回答:你不能。 Objective-C没有相应的Ruby mixins。

稍微不那么简短的回答:Objective-C确实有一些可以说具有相同风味的东西:协议。协议(一些其他语言中的接口)是一种定义一组方法的方法,一个采用该协议的类承诺实现。但协议不提供实现。这种限制阻止使用协议作为Ruby mixins的完全等价物。

更简短的答案但是,Objective-C运行时具有一个公开的API,可让您使用该语言的动态功能。然后你走出语言,但你可以拥有默认实现的协议(也称为具体协议)。弗拉基米尔的答案显示了一种方法。在这一点上,在我看来你得到Ruby mixins就好了。

但是,我不确定我会建议那样做。在大多数情况下,其他模式符合条件,而无需使用运行时游戏。例如,您可以拥有一个实现混合方法的子对象( has -a 而不是 is-a )。使用运行时是可以的,但有两个缺点:

  • 您的代码可读性较差,因为它要求读者了解的语言远远多于语言。当然你可以(并且应该)对它进行评论,但请记住,任何必要的评论都可以被视为实施缺陷。

  • 您依赖 语言的实现。当然,Apple平台是迄今为止最常见的Objective-C平台,但不要忘记Cocotron或GnuStep(或Etoilé)具有不同的运行时间,在这方面可能与Apple兼容,也可能不兼容。

作为旁注,我在下面说明类别不能将状态(实例变量)添加到类中。通过使用运行时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];
    }
}

请注意,数组中的歌手不需要具有公共超类。另请注意,一个类只能有一个超类,但许多采用的协议。最后注意,类型检查由编译器完成。

实际上,协议机制是用于mixin模式的多重继承。由于协议无法向类中添加新的实例变量,因此多重继承受到严重限制。协议仅描述了采用者必须实现的公共接口。与Ruby模块不同,它不包含实现。

这是最重要的。但是,让我们提一下类别。

声明类别不在尖括号中,而是在括号之间声明。不同之处在于可以为现有类定义类别以扩展它,而无需对其进行子类化。您甚至可以为系统类执行此操作。可以想象,可以使用类别来实现类似于mixin的东西。并且它们通常以这种方式使用,通常作为NSObject(继承层次结构的典型根)的类别,在某种程度上被称为“非正式”协议。

这是非正式的,因为1-没有类型检查由编译器完成,并且2-实现协议方法是可选的。

今天没有必要使用类别作为协议,特别是因为正式协议现在可以声明它们的某些方法对于关键字@optional是可选的,或者是@required所需的(默认)。

类别对于向现有类添加某些特定于域的行为仍然很有用。 NSString是此的共同目标。

有趣的是指出大多数(如果不是全部)NSObject设施实际上是在NSObject协议中声明的。这意味着使用NSObject作为所有类的公共超类并不是真的很有吸引力,尽管这仍然是出于历史原因通常做的,而且......因为这样做没有任何缺点。但是某些系统类(例如NSProxy NSObject

答案 2 :(得分:11)

你可以使用#include从字面上混合代码。这是不可取的,并且是针对Objective-c中的所有宗教,但是效果很好。

请不要在生产代码中执行此操作。

例如在文件中:

MixinModule.header (不应编译或复制到目标)

-(void)hello;

MixinModule.body (不应编译或复制到目标)

-(void)hello{
    NSLog(@"Hello");
}
在mixin类中

@interface MixinTest : NSObject
#include "MixinModule.header"
@end

@implementation MixinTest
#include "MixinModule.body"
@end

使用案例

#import <Foundation/Foundation.h>

int main(int argc, const char * argv[]){
    @autoreleasepool {
        [[[MixinTest new] autorelease] hello];
    }
    return 0;
}

请不要在生产代码中执行此操作。

答案 3 :(得分:1)

这是我在Objective-C中实现Mixins的看法,而不直接使用Objective-C运行时。也许这对某人有帮助:https://stackoverflow.com/a/19661059/171933