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

hac*_*nal 26 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
Run Code Online (Sandbox Code Playgroud)

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

Vla*_*vic 31

无耻插件:ObjectiveMixin

它利用了Objective-C运行时在运行时向类添加方法的能力(与类别相反,仅为编译时).看看它,它的工作非常好,并且与Ruby的mixins类似.

  • 在一群人中_"你不能."_答案,你提供的不仅仅是一种方式,而是一种自由.优秀作品. (4认同)

Jea*_*uys 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é)有不同的运行时间,在这方面可能与苹果兼容.

作为旁注,我在下面说明类别不能将状态(实例变量)添加到类中.通过使用运行时API,您也可以解除该限制.然而,这超出了这个答案的范围.

答案很长:

两个Objective-C功能看起来像可能的候选者:类别和协议.如果我正确理解这个问题,那么类别在这里并不是正确的选择.正确的功能是协议.

让我举个例子.假设您希望一堆类具有称为"唱歌"的特定能力.然后定义协议:

@protocol Singer
    - (void) sing;
@end
Run Code Online (Sandbox Code Playgroud)

现在,您可以通过以下方式声明您自己的任何类都采用该协议:

@interface Rectangle : Shape <Singer> {
    <snip>
@end

@interface Car : Vehicle <Singer> {
    <snip>
@end
Run Code Online (Sandbox Code Playgroud)

通过声明他们采用协议,他们承诺实施该sing方法.例如:

@implementation Rectangle

- (void) sing {
    [self flashInBrightColors];
}

@end

@implementation Car

- (void) sing {
    [self honk];
}

@end
Run Code Online (Sandbox Code Playgroud)

然后你使用这些类,例如:

void choral(NSArray *choir) // the choir holds any kind of singer
{
    id<Singer> aSinger;
    for (aSinger in choir) {
        [aSinger sing];
    }
}
Run Code Online (Sandbox Code Playgroud)

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

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

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

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

这是非正式的,因为1-编译器没有进行类型检查,并且实现协议方法是可选的.

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

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

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

  • "协议只描述了采用者必须实现的公共接口.与Ruby模块不同,它不包含实现." - 好吧,我认为重点是有一些代码想要在各种类中重用而不重新实现它,所以使用协议根本没有帮助... (24认同)
  • 实际上,这个答案是完全错误的.关于Objective-C的所有信息都是正确的,但问题是询问Ruby mixins,这个答案实际上并没有提供像Ruby mixin这样的东西.Ruby mixins将一个现有的方法_implementation_带到一个类中; 不只是宣言.这里的例子给`Car`一个`sing`实现`honks`和`Rectangle`一个实现`闪烁'.使用Ruby mixins,实现是_always_相同,并且始终共享.甚至类别也不能这样做,因为它们不能与其他类共享.@弗拉基米尔的回答是正确的. (8认同)

Rad*_*lin 11

你可以使用#include从字面上混合代码.这是不可取的,并且反对目标c中的所有宗教,但是完美无缺.

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

例如在文件中:

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

-(void)hello;
Run Code Online (Sandbox Code Playgroud)

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

-(void)hello{
    NSLog(@"Hello");
}
Run Code Online (Sandbox Code Playgroud)

在mixin类中:

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

@implementation MixinTest
#include "MixinModule.body"
@end
Run Code Online (Sandbox Code Playgroud)

使用案例:

#import <Foundation/Foundation.h>

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

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