26

Rubyにはモジュールがあり、モジュールを「ミックスイン」することでクラスを拡張できます。

module MyModule
  def printone
    print "one" 
  end
end

class MyClass
  include MyModule
end

theOne = MyClass.new
theOne.printone 
>> one

Objective-C では、多くのクラスに「継承」させたい共通メソッドのセットがあることがわかりました。共通クラスを作成し、その共通クラスからすべてを派生させることなく、これを達成する方法は他にあるでしょうか?

4

4 に答える 4

31

恥知らずなプラグイン: ObjectiveMixin

(コンパイル時のみのカテゴリとは対照的に) 実行時にクラスにメソッドを追加する Objective-C ランタイムの機能を利用します。それをチェックしてください、それはかなりうまく機能し、Ruby の mixins と同様の方法で機能します。

于 2011-04-12T09:07:05.323 に答える
28

編集:Objective-Cの制限について私が責任を負っていると感じる人もいるため、変更が追加されました。

短い答え:できません。Objective-C には、Ruby mixin に相当するものはありません。

やや短めの答え: Objective-C には、ほぼ間違いなく同じ趣向を持つものがあります: プロトコルです。プロトコル (他の言語ではインターフェース) は、そのプロトコルが実装を約束しているクラスが採用する一連のメソッドを定義する方法です。ただし、プロトコルは実装を提供しません。この制限により、プロトコルを Ruby ミックスインとまったく同じものとして使用することができなくなります。

さらに短い答え:ただし、Objective-C ランタイムには、言語の動的機能を操作できる API が公開されています。次に、言語の外に出ますが、デフォルトの実装を持つプロトコル (具体的なプロトコルとも呼ばれます) を持つことができます。ウラジミールの答えは、それを行う1つの方法を示しています。その時点で、Ruby ミックスインは問題ないように思えます。

ただし、それをお勧めするかどうかはわかりません。ほとんどの場合、ランタイムでゲームをプレイしなくても、他のパターンが適しています。たとえば、混合メソッド ( is- a の代わりにhas-a ) を実装するサブオブジェクトを持つことができます。ランタイムをいじっても問題ありませんが、2 つの欠点があります。

  • 読者は言語以上の知識を必要とするため、コードが読みにくくなります。もちろん、コメントすることもできます (コメントする必要があります) が、必要なコメントは実装上の欠陥と見なされる可能性があることを覚えておいてください。

  • その言語の実装に依存します。確かに、Apple プラットフォームは Objective-C の最も一般的なプラットフォームですが、ランタイムが異なる Cocotron や GnuStep (または Etoilé) を忘れないでください。これらは、その点で Apple と互換性がある場合とない場合があります。

補足として、カテゴリはクラスに状態 (インスタンス変数) を追加できないことを以下に述べます。ランタイム API を使用することで、その制限も解除できます。ただし、これはこの回答の範囲を超えています。

長い答え:

Objective-C の 2 つの機能が候補のように見えます: カテゴリとプロトコルです。質問を正しく理解していれば、カテゴリはここでは正しい選択ではありません。適切な機能はプロトコルです。

例を挙げましょう。クラスの束に「歌う」と呼ばれる特定の能力を持たせたいとします。次に、プロトコルを定義します。

@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];
    }
}

配列内の歌手は、共通のスーパークラスを持つ必要がないことに注意してください。また、クラスはスーパークラスを 1 つしか持つことができませんが、多くのプロトコルを採用していることにも注意してください。最後に、コンパイラによって型チェックが行われることに注意してください。

実際には、プロトコル メカニズムは mixin パターンに使用される多重継承です。プロトコルは新しいインスタンス変数をクラスに追加できないため、多重継承は大幅に制限されます。プロトコルは、採用者が実装する必要があるパブリック インターフェイスのみを記述します。Ruby モジュールとは異なり、実装は含まれません。

それが一番です。ただし、カテゴリについて言及しましょう。

カテゴリは、山かっこではなく、かっこの間で宣言されます。違いは、サブクラス化せずに既存のクラスを拡張するためにカテゴリを定義できることです。システムクラスに対してもそうすることができます。ご想像のとおり、カテゴリを使用して mixin に似たものを実装することができます。そして、それらは通常、(継承階層の典型的なルート) へのカテゴリとして長い間そのように使用され、NSObject「非公式」プロトコルと呼ばれるほどでした。

1-コンパイラによる型チェックは行われず、2-プロトコルメソッドの実装はオプションであるため、非公式です。

現在、カテゴリをプロトコルとして使用する必要はありません。特に、正式なプロトコルでは、メソッドの一部がキーワードでオプションである、@optionalまたは で必須 (デフォルト) であると宣言できるためです@required

カテゴリは、ドメイン固有の動作を既存のクラスに追加するのに依然として役立ちます。NSStringそのための共通のターゲットです。

また、ほとんどの機能 (すべてではないにしても) が実際にはプロトコルNSObjectで宣言されていることを指摘するのも興味深いことです。NSObjectこれは、すべてのクラスに共通のスーパークラスとして使用することは実際には魅力的ではないことを意味しますNSObjectが、これは歴史的な理由から今でも一般的に行われています.そうすることに欠点がないからです. ただし、 などの一部のシステム クラスはNSProxyではありません NSObject

于 2010-03-21T09:26:33.850 に答える
11

#include を使用して、文字通りコードを混在させることができます。これはお勧めできず、objective-c のすべての宗教に反しますが、完全に機能します。

製品コードでは使用しないでください。

たとえば、次のファイルで:

MixinModule.header (コンパイルしたり、ターゲットにコピーしたりしないでください)

-(void)hello;

MixinModule.body (ターゲットにコンパイルまたはコピーしないでください)

-(void)hello{
    NSLog(@"Hello");
}

ミックスインクラスで:

@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;
}

製品コードでは使用しないでください。

于 2012-07-20T21:06:08.580 に答える
1

これは、Objective-C ランタイムを直接使用せずに、Objective-C で Mixins を実装するという私の見解です。多分それは誰かに役立つでしょう: https://stackoverflow.com/a/19661059/171933

于 2013-12-23T21:54:29.763 に答える