Подклассы и приведение в цель C
-
06-07-2019 - |
Вопрос
Сегодня я столкнулся со странной проблемой. Я создал подкласс UIView и добавил только 1 метод в код шаблона, предоставляемый xcode.
@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);
Когда компилятор отправляет сообщение FloatView *
, а не UIView *
, он может найти точную сигнатуру метода. Он может видеть, что действительно ожидает CGFloat
и не продвигает аргумент для double
. В результате все работает правильно. Р>
Кроме того, если UIView *
содержит объявление метода, которое принимает CGFloat
, компилятор будет вызывать метод соответствующим образом. Подводя итог, это не проблема полиморфизма; это проблема с отсутствующей подписью метода.