web-dev-qa-db-ja.com

「クラスA」をそのサブクラス「クラスB」にキャストする方法-Objective-C

NSObjectのサブクラスである「ClassA」を定義して使用するフレームワークを使用しています。いくつかの変数と機能を追加したいので、当然、「ClassA」のサブクラスである「ClassB」を作成しました

今私の問題はこれです。このフレームワーク内のメソッドの多くは、サブクラスにキャストしたい「ClassA」のインスタンスを返します。

たとえば、次のメソッドを使用します。

- (ClassA *)doSomethingCool:(int)howCool

今私のコードでこれを試します:

ClassB * objB;
objB = (ClassB *)doSomethingCool(10); 

NSLog(@"objB className = %@", [objB className]);

これは問題なく実行されます。コンパイルエラーや実行時エラーなどはありません。しかし、私にとって本当に奇妙なのは出力です:

>> "objB className = ClassA"

キャストは明らかに失敗しました。この時点で何が起こっているのかわかりません... objBは「ClassB」と入力されていますが、classNameは「ClassA」であり、「ClassB」メソッドには応答しません。

これがどのように可能かわからない...誰かが私がここで間違っていることを知っていますか?

私が求めているものとは正反対の同様の投稿を見つけました here

26
nrj

Objective-Cでオブジェクト変数をキャストすることは、通常は間違いです(適切な場合がいくつかありますが、このようなことはありません)。オブジェクトをキャストしていないことに注意してください— pointerをオブジェクトにキャストしています。次に、ClassB*型のポインターがありますが、それでもClassAの同じインスタンスを指します。指摘されたことはまったく変わっていません。

ClassAのインスタンスをClassBに変換する場合は、ClassAからClassBインスタンスを作成できるClassBコンストラクターメソッドを記述する必要があります。インスタンス変数を追加する必要がある場合は、これが最良の選択です。

しかし、ジェイソンが言ったように、最初にカテゴリーを試すことはしばしば良い考えです。

26
Chuck

スーパークラスをそのサブクラスにキャストすることはできません。追加された変数/プロパティまたはメソッドは実際には実装されません。サブクラスで定義したメソッドを使用してメッセージを送信しようとするとすぐに、ランタイム例外が発生し、アプリケーションが終了します。安全にキャストできるのは、特定の方向からより一般的な方向の1方向のみです。つまり、ClassAのメソッドとプロパティのみを使用し、その逆は使用せずに、ClassBをClassAに安全にキャストできます。

このように考えてください。Car(親クラス)とFourDoorSedan(サブクラス)があります。どの車にもエンジンと2つのドアがあります。今、あなたがどこかから車を手に入れているとしましょう、そしてそれは本当にあなたがそれについて知っているすべてです。あなたはオペレーターにその車はFourDoorSedanであると伝えますが、実際にはそうではありません。それで、オペレーターがopenBackPassengerDoorのようなことをすると、どうなりますか?扉は2つしかない!!ここも同じです。

ClassAに小さな機能を追加したいだけの場合は、Objective-Cのカテゴリーを確認してください。おそらく、それらが必要なものであり、キャストは必要ありません。ただし、注意事項がないわけではないため、ドキュメントを注意深く読んでください。

19
Jason Coco

既存のオブジェクトにメソッドを追加したいだけの場合、サブクラス化は正しい方法ではありません。 category と呼ばれる言語機能を使用して、既存のクラス(およびそのインスタンス)にメソッドを追加できます。

例:

//"ClassA+doSomethingCool.h"
@interface ClassA (doSomethingCool)
- (ClassA *)doSomethingCool:(int)howCool;
@end


//"ClassA+doSomethingCool.m"
@implementation ClassA (doSomethingCool)
- (ClassA *)doSomethingCool:(int)howCool
{

}
@end
5
user151660

キャストは、あるタイプのオブジェクトから別のタイプのオブジェクトには変換されません。ファクトリパターンを利用しない限り、一部のライブラリでオブジェクトのタイプを変更して別のタイプのオブジェクトを作成することはできません。

2
Dmitriy

返されたオブジェクトでサブクラスメソッドの1つを呼び出すとどうなりますか?

ClassB * objB;
objB = (ClassB *)doSomethingCool(10);
[objB subClassMethod];

Obj-Cはかなり自由であり、型で負けます。たとえば、事前に型を知る必要さえありません。すべてのClassB参照をidに変更できます。私はあなたが期待されたタイプを持っていることを知っている方が安全であることを知っていますが、あなたのコードが正しいならそれは重要ではありません。

0
Henry