Objective-C は Ruby と同様に Mixin をサポートしていますか?
-
21-09-2019 - |
質問
Ruby にはモジュールがあり、モジュールを「混合」することでクラスを拡張できます。
module MyModule
def printone
print "one"
end
end
class MyClass
include MyModule
end
theOne = MyClass.new
theOne.printone
>> one
Objective-C では、多くのクラスに「継承」させたい一連の共通メソッドがあることがわかりました。共通クラスを作成し、その共通クラスからすべてを派生することなく、これを実現できる他の方法は何でしょうか?
解決
編集:一部の人々は、Objective-C の制限について私に責任があると感じているため、変更を追加しました。
短い答え:あなたはできません。Objective-C には、Ruby ミックスインに相当するものがありません。
短い答えが少し少ない:Objective-C には、おそらく同じ風味を持つものがあります。プロトコル。プロトコル (他の言語ではインターフェイス) は、そのプロトコルを採用するクラスが実装にコミットしているメソッドのセットを定義する方法です。ただし、プロトコルは実装を提供しません。この制限により、プロトコルを Ruby ミックスインとまったく同等のものとして使用することができなくなります。
さらに短い答え: ただし、Objective-C ランタイムには、言語の動的な機能を操作できる公開 API があります。その後、言語の外に出ますが、デフォルトの実装を備えたプロトコル (具象プロトコルとも呼ばれます) を使用できます。ウラジミールの答えは、それを行うための1つの方法を示しています。この時点では、Ruby ミックスインは問題なく取得できているようです。
ただし、それをお勧めするかどうかはわかりません。ほとんどの場合、ランタイムでゲームをプレイしなくても、他のパターンが当てはまります。たとえば、混合メソッド (があります の代わりに は、です)。ランタイムを使って遊ぶのは問題ありませんが、2 つの欠点があります。
読者に言語以上の知識が必要となるため、コードが読みにくくなります。確かにコメントすることはできます (すべきです)。ただし、必要なコメントは実装上の欠陥としてみなされる可能性があることに注意してください。
あなたは依存しています それ 言語の実装。確かに、Apple プラットフォームは Objective-C にとって最も一般的なものですが、ランタイムが異なる Cocotron や GnuStep (または Etoilé) を忘れないでください。これらのプラットフォームは、その点で Apple のプラットフォームと互換性がある場合とそうでない場合があります。
補足として、カテゴリはクラスに状態 (インスタンス変数) を追加できないことを以下に述べます。ランタイム API を使用すると、その制限も解除できます。ただし、これはこの回答の範囲を超えています。
長い答え:
2 つの 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];
}
}
配列内のシンガーは共通のスーパークラスを持つ必要がないことに注意してください。クラスはスーパークラスを 1 つだけ持つことができますが、多くのプロトコルが採用されていることにも注意してください。最後に、型チェックはコンパイラによって行われることに注意してください。
実際、プロトコル メカニズムは、ミックスイン パターンに使用される多重継承です。プロトコルでは新しいインスタンス変数をクラスに追加できないため、多重継承は厳しく制限されています。プロトコルは、アダプターが実装する必要があるパブリック インターフェイスを記述するだけです。Ruby モジュールとは異なり、実装は含まれていません。
それがほとんどです。ただし、カテゴリについては触れておきましょう。
カテゴリは山括弧内ではなく括弧内で宣言されます。違いは、既存のクラスに対してカテゴリを定義して、サブクラス化せずに拡張できることです。システム クラスに対してもこれを行うことができます。ご想像のとおり、カテゴリを使用して mixin に似たものを実装することができます。そして、それらは通常、次のカテゴリとして長い間そのように使用されてきました。 NSObject
(継承階層の典型的なルート)、「非公式」プロトコルと呼ばれるほどでした。
1- コンパイラによる型チェックは行われず、2- プロトコル メソッドの実装はオプションであるため、これは非公式です。
現在では、特に正式なプロトコルではメソッドの一部がオプションであることをキーワードで宣言できるため、カテゴリをプロトコルとして使用する必要はありません。 @optional
または必須 (デフォルト) @required
.
カテゴリは、既存のクラスにドメイン固有の動作を追加するのに依然として役立ちます。 NSString
それは一般的なターゲットです。
また、(すべてではないにしても)ほとんどが、 NSObject
施設は実際には NSObject
プロトコル。これは、実際に使用する必然性がないことを意味します NSObject
すべてのクラスに共通のスーパークラスとして使用されますが、これは歴史的な理由から今でも一般的に行われています...そうすることに何のデメリットもないからです。ただし、次のような一部のシステム クラスは、 NSProxy
, 、 は ない NSObject
.
他のヒント
恥知らずなプラグ: ObjectiveMixin の
これは(のみコンパイル時であるカテゴリとは対照的に)実行時にクラスにメソッドを追加するのObjective-Cランタイムの能力を利用します。 、それはかなり良いが動作し、Rubyのミックスインと同様の方法でそれをチェックアウト。
#include を使用すると、文字通りコードをミックスインできます。これはお勧めできませんし、目的 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;
}
製品コードではこれを行わないでください。
この直接Objective-Cランタイムを使用せずに、Objective-Cでミックスインを実装する上で私の感想です。多分それは誰かに便利です: https://stackoverflow.com/a/19661059/171933 の