3

私のプログラムには、クラスがありますClassA。派生クラスを作成したいと思いますClassB。私のプログラムには のインスタンスを返す関数がClassAあり、これらの戻り値を使用して のインスタンスを作成したい場合がありますClassB。単純なダウンキャストを行ってもコンパイラ エラーは発生しませんが、実行時エラーが発生します。これは、ポインターとオブジェクトのキャストに関する問題に関連しているようです。この場合に行うこと。

次に、C++ のコピー コンストラクターを模倣しようとしましたが、コピーされた変数がスコープ外に渡されたり、解放されたりして、再び実行時エラーが発生しました。

カテゴリの使用も検討しましたが、これは次の 2 つの理由から適切ではないようです。

  1. 何よりもまず、新しいクラス メンバーをカテゴリに追加することはできません。これができないと、少し複雑になります。
  2. カテゴリを介して追加されたメソッドは、クラスのすべてのインスタンスで使用できるようになります。厳密に言えば、これは問題ではありませんが、追加したいメソッドの特定の側面が一般的な設定で壊れる可能性があるため、私にはうまくいきません。つまり、これらのタイプのオブジェクトで何をする必要があるのか​​ 、何らかの形で特定されています。

私はまだObjective-Cを学んでいます。フォーラムに同様の質問がいくつかあることは知っていますが、答えはカテゴリにつながり、行き止まりまたは行き止まりになりましたが、あまり助けがなかったので、単純に再投稿する必要があると感じました.

4

4 に答える 4

4

私のプログラムには、クラスAと言うクラスがあります。ClassB などの派生クラスを作成したいと思います。

@class MONClassA;

@interface MONClassB : MONClassA
{
    int anotherVariable;
}

//...
- (id)initWithMONClassA:(MONClassA *)classA anotherVariable:(int)anotherVar;

@end

私のプログラムには ClassA のインスタンスを返す関数があり、場合によってはこれらの戻り値を使用して ClassB のインスタンスを作成したいと考えています。

C ++で行うのと同じ方法で(それがあなたのバックグラウンドのように聞こえるため)、新しいインスタンスで型を昇格させます。

//...
MONClassA * a = SomeLibFunction();
MONClassB * b = [[MONClassB alloc] initWithMONClassA:a anotherVariable:???];
//...

単純なダウンキャストを行ってもコンパイラ エラーは発生しませんが、実行時エラーが発生します。これは、ポインターとオブジェクトのキャストに関する問題に関連しているようです。この場合に行うこと。

正しい - それは間違ったことです。

次に、C++ のコピー コンストラクターを模倣しようとしましたが、コピーされた変数がスコープ外に渡されたり、解放されたりして、再び実行時エラーが発生しました。

上記の実装を参照して、objc で型をプロモートする方法を確認してください。これはもちろん、初期化のための適切な実装も定義することを前提としています (cpp コンストラクターおよびコピー コンストラクターと同等)。

カテゴリの使用も検討しましたが、これは正しくないようです...

あなたの本能はおそらく正しいでしょう。この言語機能はよく誤用されます。

于 2011-05-29T22:14:03.460 に答える
1

ClassAClassB、およびClassCは次のように定義されているとしましょう。

@interface ClassA : NSObject {

}
- (void)methodA;
@end

@interface ClassB : ClassA {

}
- (void)methodB;
@end

@protocol ClassCProtocol <NSObject>
- (void)protocolMethodC;
@end

@interface ClassC : ClassA <ClassCProtocol> {

}
@end

興味深いことに、プロトコルから継承する@protocol名前付きオブジェクトと、プロトコルのサブクラスでプロトコルに準拠するオブジェクトも定義しました(つまり、プロトコルに準拠するすべてのオブジェクトは、プロトコルを実装することが保証されています)。方法)。ClassCProtocol<NSObject>ClassCClassA<ClassCProtocol>-protocolMethodC

最初に注意すべきことは、Objective-C には、C++ にあるのと同じ意味での派生クラスのようなものは実際には存在しないということです。単一の継承しかないため、通常ClassB、 のサブクラスClassA、またはClassCのサブクラスですClassA

が、状況に応じて、、またはMDLoadObject()のインスタンスを返す関数であると仮定して、次のコードを使用します。ClassAClassBClassC

ClassA *MDLoadObject() {
    ClassA *object = nil;
    if (...) {
       object = [[[ClassA alloc] init] autorelease];
    } else if (...) {
       object = [[[ClassB alloc] init] autorelease];
    } else {
       object = [[[ClassC alloc] init] autorelease];
    }
    return object;
}

@interface MDAppController : NSObject {

}
- (void)loadObject:(id)sender;
@end

@implementation MDAppController

- (void)loadObject:(id)sender {
   ClassA *instanceOfClassABorC = MDLoadObject();

   if ([instanceOfClassABorC isKindOfClass:[ClassB class]]) {
       [(ClassB *)instanceOfClassABorC methodB];
   } else if ([instanceOfClassABorC isKindOfClass:[ClassC class]]) {
       [(ClassC *)instanceOfClassABorC protocolMethodC];
   } else if ([instanceOfClassABorC respondsToSelector:@selector(protocolMethodC)) {
       [(ClassC *)instanceOfClassABorC protocolMethodC];
   } else {

   }
}
@end

クラスClassBとの最上位の共通祖先ClassCはであるため、 のインスタンスを返す関数ClassAを定義します。(単一継承では、 と のすべてのインスタンスも のインスタンスであることが保証されていることに注意してください)。MDLoadObject()ClassAClassBClassCClassA

このメソッドは、Objective-C のダイナミズムを示しており、この時点で関数-loadObject:から返されたオブジェクトの型を調べる方法がいくつかあります。メソッドはキャストなしで書くこともでき、実行時に問題なく実行されることにMDLoadObject()注意してください。-loadObject:

- (void)loadObject:(id)sender {
   ClassA *instanceOfClassABorC = MDLoadObject();

   if ([instanceOfClassABorC isKindOfClass:[ClassB class]]) {
       [instanceOfClassABorC methodB];
   } else if ([instanceOfClassABorC isKindOfClass:[ClassC class]]) {
       [instanceOfClassABorC protocolMethodC];
   } else if ([instanceOfClassABorC respondsToSelector:@selector(protocolMethodC)) {
       [instanceOfClassABorC protocolMethodC];
   } else {

   }
}
于 2011-05-29T21:48:38.960 に答える
1

ClassA のインスタンスを返すメソッドがあるようですが、代わりに ClassB のインスタンスを返したいとします。

戻り値のキャストが機能しない理由は、メソッドによって返されるインスタンスが実際には ClassB のインスタンスではなく、実際には ClassA のインスタンスにすぎないためです。確かにキャスト自体は「機能」しますが、基になるインスタンスには、ClassB のメンバーを適切に処理するために必要なデータがありません。

ClassB のインスタンスを返すようにメソッドを変更できない (または変更したくない) と仮定すると、(カテゴリ以外の) 1 つのオプションは、ClassA のインスタンスを取るように ClassB を変更することです。ClassB は、ClassA によって実装されたすべてのメソッドをオーバーライドし、最初に渡された ClassA のインスタンスに従います。例えば:

@implementation ClassB
- (id)initWithClassA:(ClassA*)a
{
    self = [super init];
    if(!self)
    {
        return nil;
    }

    myClassA = [a retain];

    return self;
}

- (void)dealloc
{
    [myClassA release];
}

- (void)ClassAMethod1
{
    [myClassA ClassAMethod1];
}

- (int)ClassAMethod2
{
    return [myClassA ClassAMethod2];
}

- (void)ClassBMethod1
{
    // implement the method here
}
@end

次に、元のメソッドによって返された ClassA のインスタンスを使用して、ClassB のインスタンスを作成します。これにより、基本的に ClassA の元のインスタンスがラップされ、ClassB のインスタンスに変わります。

于 2011-05-29T18:06:55.443 に答える
0

IDタイプを使用してみましたか?任意のタイプのオブジェクトを割り当てることができます。

于 2011-05-29T18:07:34.997 に答える