3

次の名前のクラスを作成しましたFoo

@interface Foo:NSObject{
     int myInt;
}
@property int myInt;
@end

およびFoo名前付きのサブクラスBar

@interface Bar:Foo{
     NSString *myString;
}
@property (copy) NSString *myString;
@end

Bar次のように、Fooオブジェクトとして配列に格納しようとしています。

-(void)createBar{
     Foo *object = [[Bar alloc]init];

     // myArray is an instance of NSMutableArray
     [myArray addObject:object];
}

私は実際に複数のサブクラスを持っているのでこれを行っていますFoo(それらすべてをリストしたくありません)。配列からオブジェクトを取得し、そのオブジェクトにメッセージを送信してmyString変数を取得すると、アプリケーションは何もしません。 例:

-(NSString *)getStringFromFooAtIndex(NSUInteger)index{
     Foo *object = [myArray objectAtIndex:index];

     return [object myString];
}

「メッセージ」がどのように機能するか誤解していますか?私は、オブジェクトにメッセージを送信でき、そこにあるかどうかに関係なく、オブジェクトを呼び出すことができると想定していました。これを他の方法で行う必要がありますか?配列はすべての異なるタイプのFoo子クラスを保持し、それらをそこに格納するために必要です。

4

3 に答える 3

3

オブジェクトにメッセージを送信でき、そこにあるかどうかに関係なくそれを呼び出すことができると想定していました。

実際、任意のメッセージを任意のオブジェクトに送信できます。それが Objective-C の楽しみの 1 つです。変数の型( Foo *Bar *id、またはその他) は、メッセージの送信には影響しません。変数が指すオブジェクトは、そのクラスを認識しています。対応するメソッドのルックアップは、そのクラスを介して実行時に行われます。コンパイラは、角かっこで囲まれた式を関数の呼び出しに変換しますobjc_msgSend

ビルド時に、「'Foo' は 'myString' に応答しない可能性があります」という警告が表示されるはずです[object myString]。コンパイラは、に対応するメソッドを持つクラスがどこかに少なくとも 1 つあるmyStringことを認識しており、コンパイル時にtimeFooはそれらの 1 つではないようですがFoo、実行時にメッセージに対して何かを実行できないことを保証することはできません。メッセージは実行時に独自の方法で解決できます。変数の型を に変更するidと、警告が消えることに注意してください。コンパイラは、使用可能なメソッドについて判断できなくなります。

myString 送信先のオブジェクトが応答しないことが判明した場合(つまり、オブジェクトが本当にではFooなく であるBar場合、または をFoo実装していない のサブクラスの1 つである場合myString)、例外が発生します。NSObjectこれは、認識されないメッセージに対する ( から継承するものによる) デフォルトの応答です。

メッセージを送信する必要があるオブジェクトの異種コレクションがある場合は、最初に各オブジェクトをテストすることをお勧めします。jstevenco が提案したように実行し、機能をテストできます。

if( [object respondsToSelector:@selector(myString)] ){

または身元をテストします。

if( [object isKindOfClass:[Bar class]] ){

後者は、オブジェクトがBarまたはBarのサブクラスのいずれかである場合に合格します。指定したクラスのみをテストするために使用isMemberOfClass:します。

于 2011-12-13T19:23:49.683 に答える
1

メッセージング ロジックの処理方法の概要については、こちらを参照してください。オーバーライドしないと、最終的にエラーが発生します-(void)doesNotRecognizeSelector:(SEL)aSelector

オブジェクトのポリモーフィックなコレクションを作成する場合は、最初にオブジェクトをテストしてからrespondsToSelector:、メソッドを直接呼び出すかperformSelector:、必要に応じてバリアントの 1 つを使用する必要があります。もう 1 つの可能性は、myString何もしない基本クラスに の実装を含めることです。

于 2011-12-13T18:11:28.607 に答える
0

私は問題が何であるかを理解しました。

私の AppDelegate.m ファイルには、次のメソッドがあります。

- (void)createFoo{
     Foo *foo = [[Bar alloc]init];

     [myArrayController add:foo];
}

これは、「foo」オブジェクトを配列コントローラーに挿入していました。このメソッドも定義しています。

- (void) insertObject:(Foo *)object intoMyArrayControllerAtIndex:(NSUInteger)index{
     // handles the inserting of the object as well as working with the undo manager
}

さて、呼び出し[myArrayController add:foo]が問題の原因でした。その行を次のように置き換えると[self insertObect:foo intoMyArrayControllerAtIndex:0]、すべてが完全に機能します。

ありがとうございました。お時間を無駄にして申し訳ありませんでした。

于 2011-12-14T13:50:02.233 に答える