5

isKindOfClass:を呼び出してクラスのタイプを確認するか、respondsToSelector:を介して探しているメソッドをサポートしているかどうかを確認するだけで「ダックタイピング」アプローチを採用する方が適切ですか?

これが私が考えているコードで、両方の方法で書かれています。

for (id widget in self.widgets)
{
    [self tryToRefresh:widget];

    // Does this widget have sources? Refresh them, too.
    if ([widget isKindOfClass:[WidgetWithSources class]])
    {
        for (Source* source in [widget sources])
        {
            [self tryToRefresh:source];
        }
    }
}

または:

for (id widget in self.widgets)
{
    [self tryToRefresh:widget];

    // Does this widget have sources? Refresh them, too.
    if ([widget respondsToSelector:(@selector(sources))])
    {
        for (Source* source in [widget sources])
        {
            [self tryToRefresh:source];
        }
    }
}
4

4 に答える 4

6

状況によります!

私の大まかなルールは、これは私だけのものなのか、それとも他の誰かに渡すのかということです。

あなたの例でrespondsToSelector:は、問題ありません。あなたが知る必要があるのは、オブジェクトにそのメッセージを送信できるかどうかだけなので、結果を使って何かを行うことができます。クラスはそれほど重要ではありません。

一方、そのオブジェクトを他のコードに渡す場合は、送信しようとしているメッセージが必ずしもわからない場合があります。そのような場合、オブジェクトを渡すためにオブジェクトをキャストしている可能性があります。これは、isKindOfClass:キャストする前に実際にオブジェクトが存在するかどうかを確認するための手がかりになる可能性があります。

考慮すべきもう1つのことは、あいまいさです。respondsToSelector:オブジェクトがメッセージに応答することを通知しますが、オブジェクトが予想とは異なるタイプを返す場合、誤検知が発生する可能性があります。たとえば、メソッドを宣言するオブジェクトは次のとおりです。

- (int)sources;

テストに合格しrespondsToSelector:ますが、for-inループで戻り値を使用しようとすると例外が生成されます。

それが起こる可能性はどのくらいありますか?それはあなたのコード、あなたのプロジェクトの大きさ、あなたのAPIに対してコードを書いている人の数などに依存します。

于 2012-07-16T18:36:46.430 に答える
5

使用するのは少し慣用的なObjectiveCrespondsToSelector:です。Objective Cは非常に動的であるため、クラス構造に関する設計時の仮定は、実行時に必ずしも水を保持するとは限りません。respondsToSelector:クラスのタイプをクエリする最も一般的な理由(何らかの操作を実行するかどうか)へのショートカットを提供することで、これを回避します。

一般に、同じように魅力的な選択肢がいくつかある場合は、読みやすさを重視してください。この場合、それは意図について考えることを意味します。それが特にであるWidgetWithSourcesかどうかsourcesにしますか、それともセレクターがあることだけを気にしますか?後者の場合は、を使用しますrespondsToSelector:。前者の場合、場合によってはisKindOfClass.可読性を使用することは、この場合、読者にの型の同等WidgetWithSources性とを呼び出す必要性との関係を作成するように求めていないことを意味しますsourcesrespondsToSelector:読者のためにその接続を確立し、あなたが実際に何を意図していたかを彼らに知らせます。それはあなたの仲間のプログラマーに対する小さな親切な行為です。

編集:@benzadoの答えはうまく一致しています。

于 2012-07-16T18:43:00.787 に答える
3

@ Timと@benzadoからの良い答えです。これがテーマのバリエーションで、前に2つのケースを最初に取り上げました。

  • ある時点で、個別のクラスへの参照があり、それらを別の方法で必要とする場合、これはおそらく次の場合ですisKindOfClass:。たとえば、色は、のNSDataシリアル化NSColorとして、またはNSString標準の1つを使用した値として設定に格納される場合があります。名前; NSColorこの場合、オブジェクトの戻り値で値を取得するのisKindOfClass:がおそらく適切です。
  • 単一のクラスへの参照があるが、時間の経過とともに異なるバージョンの異なるメソッドが異なるメソッドをサポートしている場合は、次のことを検討してrespondsToSelector:ください。たとえば、多くのフレームワーククラスは、OSの新しいバージョンに新しいメソッドを追加し、Appleの標準的な推奨事項は、respondsToSelector:(およびOSバージョンチェックではありません)。
  • 個別のクラスへの参照があり、それらが非公式のプロトコルに準拠しているかどうかをテストしている場合は、次のようにします。

    1. これが制御するコードである場合は、正式なプロトコルに切り替えて、テストとして使用できconformsToProtocol:ます。これには、名前だけでなくタイプもテストできるという利点があります。そうでなければ
    2. これが制御しないコードである場合は、を使用しますrespondsToSelector:が、これは同じ名前のメソッドが存在することをテストするだけであり、同じタイプの引数を取ることはないことを認識しています。
于 2012-07-16T19:44:36.880 に答える
2

どちらかをチェックすることは、あなたがハック的な解決策を作ろうとしているという警告かもしれません。ウィジェットはすでに彼のクラスとセレクターを知っています。

したがって、3番目のオプションはリファクタリングを検討することかもしれません。このロジックをに移動する[widget tryToRefresh]と、よりクリーンになり、将来のウィジェットで追加のバックグラウンドロジックを実装できるようになります。

于 2012-07-16T19:14:49.580 に答える