Строгая и свободная типизация при переопределении метода
У меня есть класс AddressCard из примера в разделе "Программирование в Objective C", и я реализую метод isEqual:.
Сигнатура этого метода в NSObject использует свободную типизацию для параметра:
- (BOOL)isEqual:(id)anObject
OTOH, пример кода в книге использует строгую типизацию:
- (BOOL) isEqual:(AddressCard *) aCard
Я не уверен, что полностью понимаю, что делает компилятор в этом случае. Я попытался сравнить AddressCard с NSString ([aCard isEqual: @"Foo"]
) ожидание либо ошибки времени выполнения (если система использует мой метод), либо что система вызовет версию IsEqual от NSObject.
Вместо этого мой метод был вызван (хотя параметр был NSString, а не AddressCard) и вызвал исключение, когда мой IsEqual: попытался вызвать метод, специфичный для AddressCard:
- (BOOL) isEqual:(AddressCard *) aCard {
if ([name isEqualToString: [aCard name]] && /*here I get the error*/
[email isEqualToString:[aCard email]]) {
return YES;
}else {
return NO;
}
}
В чем дело? Как же NSString передается методу, который ожидает чего-то другого? Можно ли изменить сигнатуру метода при переопределении?
2 ответа
Среда выполнения различает сообщения по их селектору. Все методы с одинаковым именем имеют одинаковый селектор. Аргументы метода не влияют на селектор. В вашем случае селектор isEqual:
,
Это из Apple "Язык программирования Objective-C" (выделено мной):
Подпрограмма обмена сообщениями имеет доступ к реализациям методов только через селекторы, поэтому она обрабатывает все методы одним и тем же селектором. Он обнаруживает тип возвращаемого значения метода и типы данных его аргументов из селектора. Следовательно, за исключением сообщений, отправляемых статически типизированным получателям, динамическое связывание требует, чтобы все реализации методов с одинаковыми именами имели одинаковый тип возврата и одинаковые типы аргументов. (Статически типизированные получатели являются исключением из этого правила, поскольку компилятор может узнать о реализации метода из типа класса.)
Другими словами: изменение сигнатуры существующего метода не является хорошей формой (IMO), но это нормально, если вы статически вводите получателей этих методов (в вашем случае это означает, что aCard
должен быть объявлен как AddressCard *
). Для среды выполнения это не проблема.
К сожалению, вы не упоминаете, дает ли компилятор предупреждение, потому что вы передаете NSString *
где он ожидает AddressCard *
, Я ожидаю, что это так.
Мое лучшее предположение: все, что видит компилятор, - это метод, который ожидает, что указатель вызывается с параметром указателя. Нет проблем для компилятора.