web-dev-qa-db-ja.com

Objective-Cでプロトコルのカテゴリを定義していますか?

Objective-Cでは、カテゴリのある既存のクラスにメソッドを追加できます。

@interface NSString (MyCategory)
- (BOOL) startsWith: (NSString*) prefix;
@end

プロトコルでこれを行うことも可能ですか?つまり、次のようなNSStringプロトコルがあった場合:

@interface <NSString> (MyCategory)
- (BOOL) startsWith: (NSString*) prefix;
@end

NSObject(クラス)にいくつかの拡張機能があり、パブリックNSObjectメソッドのみを使用しており、これらの拡張機能がプロトコルを実装するオブジェクトでも機能するため、これを行います。

さらなる例として、オブジェクトの説明をログに出力するメソッドlogDescriptionを記述したい場合はどうすればよいですか。

- (void) logDescription {
    NSLog(@"%@", [self description]);
}

もちろん、このメソッドをNSObjectに追加することもできますが、NSObjectから継承しないクラスが他にもあります。 NSProxy。このメソッドはprotocolのパブリックメンバーのみを使用するため、プロトコルに追加するのが最善です。

編集:Java 8には、これにインターフェースの「仮想拡張メソッド」が含まれています: http://cr.openjdk.Java.net/~briangoetz/lambda/Defender%20Methods% 20v4.pdf 。これはまさにObjective-Cで実行したいことです。この質問がこれほど注目されることはありませんでした...

よろしく、Jochen

38
Jochen

extObjCにはNEATESTのものがあります プロトコル/カテゴリで実行できます...最初は_@concreteprotocol_...です。

  • プロトコル内のメソッドのデフォルト実装を提供できる「具体的なプロトコル」を定義します。
  • _@protocol_ブロックはヘッダーファイルに存在し、対応する_@concreteprotocol_ブロックは実装ファイルに存在する必要があります。
  • このプロトコルに準拠すると宣言したオブジェクトは、そのメソッド実装を受け取りますが、同じ名前のメソッドがすでに存在しない場合に限られます。

MyProtocol.h

_@protocol MyProtocol 
@required - (void)someRequiredMethod;
@optional - (void)someOptionalMethod;
@concrete - (BOOL)isConcrete;   
_

MyProtocol.m

_ @concreteprotocol(MyProtocol) - (BOOL)isConcrete { return YES; } ...
_

したがって、オブジェクト_MyDumbObject : NSObject <MyProtocol>_を宣言すると、YESisConcreteに自動的に返されます。

また、それらにはpcategoryinterface(PROTOCOL,CATEGORY)があり、「プロトコルPROTOCOLでCATEGORYという名前のカテゴリのインターフェースを定義します」。プロトコルカテゴリには、PROTOCOLへの準拠を宣言するクラスに自動的に適用されるメソッドが含まれています。 "実装ファイルでも使用する必要がある付随するマクロがあります。ドキュメントを参照してください。

最後ですが、重要ではありません直接 _@protocols_に関連していますsynthesizeAssociation(CLASS, PROPERTY)は、「関連するオブジェクトを使用してクラスのプロパティを合成します。これは、主にプロパティの追加に役立ちますカテゴリー内のクラスへ。PROPERTYは、指定されたクラス(またはその上のカテゴリー)のインターフェースで_@property_を使用して宣言されている必要があり、オブジェクトタイプでなければなりません。

このライブラリにある非常に多くのツールは、ObjCで実行できること(多重継承から... 、あなたの想像力が限界です。

19
Alex Gray

短い答え:いいえ。

長い答え:これはどのように機能しますか?想像してみてくださいcouldメソッドを既存のプロトコルに追加しますか?これはどのように機能しますか? NSCodingに別のメソッド、たとえば-(NSArray *) codingKeys;を追加したいとしましょう。このメソッドは、オブジェクトのエンコードに使用されるキーの配列を返す必須メソッドです。

問題は、すでにNSCodingを実装しているがcodingKeysメソッドを実装していない既存のクラス(たとえば、NSString)があることです。どうなりますか?この必須メッセージが、それを実装していないクラスに送信されたときに、コンパイル済みフレームワークはどうしたらよいでしょうか?

「このメソッドの定義をカテゴリを介して追加できます」または「これらのプロトコルカテゴリを介して追加されたメソッドは明示的にオプションである」と言うことができます。はい、あなたはこれを行うことができ、理論的には上で説明した問題を回避することができます。ただし、それを行う場合は、最初にそれをカテゴリにして、メソッドを呼び出す前にクラスrespondsToSelector:を確認してください。

25
Dave DeLong

プロトコルのカテゴリを定義できないことは事実ですが(既存のオブジェクトについて何も知らないので、したくありません)、コードがオブジェクトにのみ適用されるようにカテゴリを定義できます。目的のプロトコル(C++の部分テンプレートの特殊化のようなもの)を持つ指定された型。

このようなものの主な用途は、クラスのカスタマイズされたバージョンに依存するカテゴリを定義する場合です。 (Fooプロトコルに準拠するUIViewControllerサブクラスがあることを想像してください。つまり、それらはfooプロパティを持っています。私のカテゴリコードはfooプロパティを必要とするかもしれませんが、それをFooプロトコルに適用することはできません。 UIViewControllerの場合、コードはデフォルトではコンパイルされません。コードを強制的にコンパイルすると、誰かが内省を行ったり、失敗したりすると、プロトコルに依存するコードが呼び出される可能性があります。ハイブリッドアプローチは次のように機能します。

@protocol Foo
- (void)fooMethod

@property (retain) NSString *foo;
@end

@implementation UIViewController (FooCategory)

- (void)fooMethod {
    if (![self conformsToProtocol:@protocol(Foo)]) {
        return;
    }

    UIViewController<Foo> *me = (UIViewController<Foo>*) self;
    // For the rest of the method, use "me" instead of "self"
    NSLog(@"My foo property is \"%@\"", me.foo);
}
@end

ハイブリッドアプローチでは、コードを(プロトコルを実装することになっているクラスごとに)1回だけ記述でき、プロトコルに準拠していないクラスのインスタンスに影響を与えないことを確認します。

欠点は、プロパティの合成/定義が個々のサブクラスで発生する必要があることです。

22
Douglas Mayle

プロトコルが実際にメソッドを実装できないため、そうすることはあまり意味がありません。プロトコルは、いくつかのメソッドをサポートすることを宣言する方法です。プロトコルの外でこのリストにメソッドを追加すると、すべての「準拠」クラスが新しいメソッドを実装していなくても、誤って宣言します。一部のクラスがNSObjectプロトコルを実装していてもNSObjectから派生していない場合、プロトコルにメソッドを追加すると、クラスの適合性が失われます。

ただし、@protocol SpecialObject <NSObject>のような宣言を使用して、古いプロトコルを含む新しいプロトコルを作成できます。

7
Chuck

Adam Sharp 解決策を投稿しました うまくいきました。

これには3つのステップが含まれます。

  1. プロトコルに@optionalとして追加するメソッドを定義します。
  2. 拡張するオブジェクトをそのプロトコルに準拠させる。
  3. これらのメソッドを実行時にこれらのオブジェクトにコピーします。

詳細については、リンクをご覧ください。

0
Senseful

すでにカテゴリを作成している場合は、プロトコル定義をヘッダーのカテゴリ定義の直後に追加してみませんか?

つまり.

@interface NSString (MyCategory)
- (BOOL) startsWith: (NSString*) prefix;
@end

@protocol MyExtendedProtocolName <NSString>
//Method declarations go here
@end

このように、カテゴリヘッダーをインポートするクラスもプロトコル定義を取得し、それをクラスに追加できます。

@interface MyClass <OriginalProtocol,MyExtendedProtocolName>

また、NSStringをサブクラス化するときは注意が必要です。これはクラスターであり、期待した動作が常に得られるとは限りません。

0
pxl

あちこちで用語を混同しているのではないかと思います。拡張、カテゴリー、プロトコル、インターフェース、クラスは、Objective-Cではすべて異なります。 Objective-C 2.0言語 Appleは、カテゴリと拡張機能を使用することの利点と欠点を含め、違いを非常によく説明しています。

考えてみれば、概念的な意味での「カテゴリー」や「拡張」とは?クラスに機能を追加する方法です。 Objective-Cでは、プロトコルは実装されないように設計されています。したがって、最初に実装がないものの実装をどのように追加または拡張しますか?

0
slf