Я часто обнаруживаю, что знаю, что определенное свойство базового класса всегда будет определенным типом в подклассе. Например, в приведенном ниже примере свойство obj
всегда будет объектом NSString в Derived. Однако мне нужно, чтобы это свойство было более общим типом идентификатора в классе Base.
@interface Base
@property (strong, nonatomic) id obj;
@end
@implementation Base
//@synthesize obj = obj_;
@dynamic obj;
@end
@interface Derived : Base
@property (strong, nonatomic) NSString *obj;
@end
@implementation Derived
@synthesize obj = obj_;
@end
Это правильный код? Меня беспокоит, что @synthesize появляется дважды. Создает ли это два свойства, или объявление @synthesize в Derived переопределяет объявление в Base?
Edit: Изменение @synthesize на @dynamic в Base имеет больше смысла.
Изменить: Для этого требуется iOS SDK 5.
Подклассы могут изменять типы, связанные с методами. В общем, подкласс может специализировать возвращаемый тип и может сделать типы аргументов более общими. На самом деле есть имя для этого, но я не могу вспомнить, что это такое. Во всяком случае, вот рациональное:
Если у меня есть класс
@interface A
- (id)foo;
@end
и другой класс
@interface B : A
- (NSString *)foo;
@end
И у меня есть Например, B* b
, я могу привести его к A*
и при этом соответствовать сигнатуре типа метода -[A foo]
, потому что любой NSString*
также является id
.
@interface A
- (NSString *)foo;
@end
@interface B : A
- (id)foo;
@end
И у меня есть экземпляр B* b
, и я понижаю его до A*
, тогда тип [(A*)b foo]
равен NSString *
, и тем не менее фактическое значение может быть любым id
, потому что это тип, который я объявил -[B foo]
. Это нарушение системы типов.
Если у меня есть класс
@interface A
- (void)foo:(NSString *)obj;
@end
и другой класс
@interface B : A
- (void)foo:(id)obj;
@end
И у меня есть экземпляр B* b
, и я понижаю его до A*
, то любой действительный аргумент [(A*)b foo:obj]
также соответствует типу -[B foo:]
, потому что любой NSString *
также является id
.
Однако, если у меня есть следующее
@interface A
- (void)foo:(id)obj;
@end
@interface B : A
- (void)foo:(NSString *)obj;
@end
И у меня есть экземпляр B* b
, и я понижаю его до A*
, тогда я могу передать любой id
в [(A*)b foo:obj]
, но базовый класс B
ожидает только NSString*
с. И поэтому я нарушил систему типов.
Вот точка слипания. Когда вы объявляете тип свойства, вы объявляете оба типа возвращаемого значения получателя и тип аргумента установщика. Согласно приведенным выше правилам это означает, что вы не можете изменить тип свойства, потому что в одном из двух случаев вы нарушите систему типов.
<час>Выше теория. На практике я понятия не имею, применяют ли GCC или Clang эти ограничения. Возможно, они предполагают, что программист знает лучше, а неправильное обобщение или специализация типа молча сломает систему типов за вашей спиной. Вам придется экспериментировать. Но если компилятор действительно корректен, он не разрешает обобщать возвращаемые типы и специализировать аргументы. А это значит, что это не позволит изменять тип свойства.
Даже если компилятор это позволяет, вам, вероятно, не следует этого делать. Безмолвное разрушение системы типов - отличный способ внести ошибки и показатель плохой архитектуры.