Objective-C の継承とカテゴリの違いは何ですか
-
22-08-2019 - |
質問
誰かがObjective Cのカテゴリと継承の違いを説明してもらえますか?もう読んだ ウィキペディアの項目 そして、そこでのカテゴリに関する議論は、継承に関する議論と何ら変わりません。『Open iPhone Development』という本でこのテーマに関する議論も見ましたが、まだ理解できません。
解決
場合によっては、相続は価値がある以上に面倒に思えることがあります。これは、既存のクラスに何かを追加して、そのクラスの動作を変更する場合に正しく使用されます。
カテゴリを使用すると、既存のオブジェクトにもう少し機能を追加するだけで済みます。すでに述べたように、圧縮を処理する文字列クラスが必要なだけの場合は、文字列クラスをサブクラス化する必要はなく、圧縮を処理するカテゴリを作成するだけです。こうすることで、既に使用している文字列クラスの型を変更する必要がなくなります。
ヒントは、カテゴリはメソッドを追加するだけであり、カテゴリを使用してクラスに変数を追加できないという制限にあります。クラスにさらに多くのプロパティが必要な場合は、サブクラス化する必要があります。(編集:連想記憶域を使用できると思います)。
カテゴリは、機能を追加すると同時に、継承よりも合成を優先するオブジェクト指向の原則に準拠する優れた方法です。
2012 年 1 月編集
今は状況が変わりました。現在の LLVM コンパイラと最新の 64 ビット ランタイムを使用すると、iVar とプロパティをクラスに追加できます。 拡張子 (カテゴリではありません)。これにより、プライベート iVar をパブリック インターフェイスから遠ざけることができます。ただし、iVar のプロパティを宣言した場合でも、Objective-C にはプライベート メソッドのようなものがまだ存在しないため、KVC 経由でプロパティにアクセスしたり変更したりすることができます。
他のヒント
カテゴリーでは、既存のクラスにメソッドを追加することができます。そうではなく、サブクラスのNSDataはあなたのファンキーな新しい暗号化メソッドを追加するよりも、あなたはのNSDataクラスに直接追加することができます。あなたのアプリ内のすべてのNSDataオブジェクトは現在、これらのメソッドにアクセスすることができます。
CocoaDevするを見て、これがいかに便利確認するには
アクションでのObjective-Cカテゴリの好きなイラストの一つは、NSStringのです。 NSStringのは、ビューや窓の概念を持っていないFoundationフレームワークで定義されています。あなたはCocoaアプリケーションでNSStringのを使用している場合しかし、あなたはそれが– drawInRect:withAttributes:
のようなメッセージに応答に気づくでしょう。
のAppKitには、追加の描画方法を提供NSStringのためのカテゴリを定義します。カテゴリは、新しいメソッドを既存のクラスに追加することができますので、我々はまだちょうどNSStringsを扱っています。 AppKitのではなく、サブクラス化によって描画実装されている場合、私たちは「AppKitStrings」または「NSSDrawableStrings」またはそのような何かに対処する必要があると思います。
カテゴリーでは、既存のクラスにアプリケーションまたはドメイン固有のメソッドを追加してみましょう。それは非常に強力で便利なことができます。
プログラマーとして、コード ライブラリまたはアプリケーションのソース コードの完全なセットが与えられた場合、そのコードでプログラミングの目標を達成するために必要なものは何でも変更できます。
残念ながら、これは必ずしもそうとは限りませんし、望ましいとも限りません。多くの場合、バイナリ ライブラリ/オブジェクト キットとヘッダーのセットが与えられます。
次に、クラスに新しい機能が必要になるため、いくつかのことを行うことができます。
ストッククラスの代わりに新しいクラス全体を作成します。そのすべての関数とメンバーを複製して、新しいクラスを使用するようにすべてのコードを書き直します。
ストック クラスをメンバーとして含む新しいラッパー クラスを作成し (合成)、新しいクラスを利用するようにコードベースを書き換えます。
コードを変更するためのライブラリのバイナリ パッチ (頑張ってください)
コンパイラに新しいクラスを古いクラスとして認識させ、それがメモリ内の特定のサイズや場所、特定のエントリ ポイントに依存しないことを望みます。
サブクラスの特殊化 -- 機能を追加するためにサブクラスを作成し、代わりにサブクラスを使用するようにドライバー コードを変更します -- 理論的にはほとんど問題はなく、データ メンバーを追加する必要がある場合は必須ですが、メモリ フットプリントは異なります。サブクラスで新しいコードと古いコードの両方を使用でき、基本クラスのメソッドとオーバーライドされたメソッドのどちらを使用するかを選択できるという利点があります。
必要な objc クラスを、必要なことを実行するためのメソッドを含むカテゴリ定義で変更したり、ストック クラスの古いメソッドをオーバーライドしたりできます。
これにより、ライブラリ内のエラーを修正したり、新しいハードウェア デバイスなどのメソッドをカスタマイズしたりすることもできます。これは万能薬ではありませんが、変更されていないクラス/ライブラリを再コンパイルすることなくクラス メソッドを追加できます。元のクラスはコード、メモリ サイズ、エントリ ポイントが同じであるため、レガシー アプリが壊れることはありません。コンパイラは、新しいメソッドをそのクラスに属するものとしてランタイムに配置し、元のコードと同じシグネチャを持つメソッドをオーバーライドします。
例:
シリアル ポートではなく端末に出力するクラス Bing があり、これが必要なものです。(何らかの理由で)。キットには Bing.h と libBing.so がありますが、Bing.m はありません。
Bing クラスは内部であらゆる種類の処理を実行しますが、ヘッダーにパブリック API があるだけで、すべてが何かはわかりません。
あなたは賢いので、Bing クラスの (SerialOutput) カテゴリを作成します。
[Bing_SerialOutput.m] @interface Bing (SerialOutput) // a category - (void)ToSerial: (SerialPort*) port ; @end @implementation Bing (SerialOutput) - (void)ToSerial: (SerialPort*) port { ... /// serial output code /// } @end
コンパイラはアプリにリンクできるオブジェクトを作成する必要があり、ランタイムは Bing が @selector(ToSerial:) に応答することを認識し、Bing クラスがそのメソッドで構築されたかのようにそれを使用できるようになります。データ メンバーのみのメソッドを追加することはできません。これは、基本クラスに接続されたコードの巨大な腫瘍を作成することを目的としたものではありませんが、厳密に型指定された言語よりも利点があります。
私は、カテゴリは、より軽量でありながら、継承は、既存のクラスに機能を追加する重い道であるという考えに少なくとも時点でこれらの答えのいくつかを考えます。
継承を使用すると、新しいクラス階層(すべての添えもの)を作成しているときに使用すると、既存のクラスに機能を追加する方法として選ばれたとき、おそらく仕事の多くをもたらしています。
ここでは他の誰かがそれを置くとして、あなたは、NSStringのに例のための新たな方法を追加するために、継承を使用している場合は、...、あなたはあなたがこの新しいを使用する任意の他のコード内で使用しているタイプを移動して、変更する必要があります方法。ただし、カテゴリを使用する場合は、単にサブクラス化せずに、既存のNSStringの種類でメソッドを呼び出すことができます。
同じ目的のいずれかで達成することができますが、カテゴリは私たちに簡単で、メンテナンスが少なく(おそらく)が必要オプションを与えているようです。
カテゴリは絶対に必要な状況がある場合は誰でも知っていますか?
カテゴリは、ミックスインのようなものです。あなたは、「裸のメソッド」と考えることができます。あなたはカテゴリーを追加するときは、クラスにメソッドを追加しています。 Wikipediaの記事は良いものを持っています。
この違いを見て最善の方法は、次のとおりです。 1.継承:あなたの方法で正確にそれを有効にします。 例:AsyncImageViewは、遅延ロードを実装します。どちらのUIViewを継承することによって行われます。 2.カテゴリ:ちょうどそれに余分な味を追加します。 例:私たちは、テキストフィールドのテキストからすべてのスペースを置き換えたい。
@interface UITextField(setText)
- (NSString *)replaceEscape;
@end
@implementation UITextField(setText)
- (NSString *)replaceEscape
{
self.text=[self.text stringByTrimmingCharactersInSet:
[NSCharacterSet whitespaceCharacterSet]];
return self.text;
}
@end
---それはあなたがすべての空白をエスケープするためにテキストフィールドに新しいプロパティを追加します。ただ、完全にその方法を変更することなく、そこに新たな次元を追加することなどがある。