Objective Cでのサブクラス化とキャスト
-
06-07-2019 - |
質問
今日、奇妙な問題に出会いました。 UIViewのサブクラスを作成し、xcodeが提供するテンプレートコードにメソッドを1つだけ追加しました。
@interface FloatView : UIView {
}
- (void)floatTest:(CGFloat)x;
@end
- (void)floatTest:(CGFloat)x {
NSLog(@"float was %f", x);
}
その後、appDelegateに次のようなコードがありました:
UIView *floatView = [[FloatView alloc] init];
[floatView floatTest:10.0f];
非常に簡単ですよね?これは何を印刷すべきですか? 「10.0000」のようなものになると思いましたが、「0.000000」と印刷されます。
何時間もこれに取り組んで、何が間違っていたかを把握しようとしてから、appDelegateのコードを
に変更しましたFloatView *floatView = [[FloatView alloc] init];
[floatView floatTest:10.0f];
その後のみ、予想される「10.0000」を印刷しました。これはなぜですか? FloatViewをUIViewのサブクラスとして宣言しましたが、FloatViewオブジェクトをUIViewポインターに問題なく割り当てることはできませんか?
floatViewがUIViewへのポインタとして宣言されていたとしても、それは実際にはfloatViewであり、floatTestメッセージを処理できるはずですか?私はここで完全に離れていますか?
解決
実際、ポリモーフィズムは期待どおりに機能しています。動作しなかった場合、何も印刷されませんでした(例では、0.0000が印刷されています)。問題は、インスタンスが実際に testFloat:10.0f
メッセージに応答する一方で、コンパイラがメソッド宣言を静的に見ることができないためです( UIView
クラスはメソッド)、メソッドが引数として ...
を取り、 id
を返すと仮定します。
CGFloat
が可変数の引数( ...
)を必要とするメソッドに渡されると、 double
に昇格されます。したがって、受信メソッドには double
引数が渡され、 float
であると見なされ、正しく印刷されません。
NSLog
行を次のように変更することにより、この動作を確認できます。
NSLog(@"%f", *(double*)&x);
コンパイラが UIView *
ではなく FloatView *
にメッセージを送信すると、メソッドの正確なシグネチャを見つけることができます。本当に CGFloat
を期待し、引数を double
に昇格させないことがわかります。その結果、正常に機能します。
さらに、 UIView *
に CGFloat
をとるメソッド宣言が含まれている場合、コンパイラはメソッドを適切に呼び出します。要約すると、これはポリモーフィズムの問題ではありません。メソッド署名の問題がありません。