Переопределение объявлений @property в Objective-C

Я часто обнаруживаю, что знаю, что определенное свойство базового класса всегда будет определенным типом в подклассе. Например, в приведенном ниже примере свойство 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.

25
задан titaniumdecoy 16 August 2011 в 22:57
поделиться

1 ответ

Подклассы могут изменять типы, связанные с методами. В общем, подкласс может специализировать возвращаемый тип и может сделать типы аргументов более общими. На самом деле есть имя для этого, но я не могу вспомнить, что это такое. Во всяком случае, вот рациональное:

Типы возвращаемых данных

Если у меня есть класс

@interface A
- (id)foo;
@end

и другой класс

@interface B : A
- (NSString *)foo;
@end

И у меня есть Например, B* b, я могу привести его к A* и при этом соответствовать сигнатуре типа метода -[A foo], потому что любой NSString* также является id.

1137 Однако я не могу сделать это более обобщенным. Если вместо этого у меня есть

@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 эти ограничения. Возможно, они предполагают, что программист знает лучше, а неправильное обобщение или специализация типа молча сломает систему типов за вашей спиной. Вам придется экспериментировать. Но если компилятор действительно корректен, он не разрешает обобщать возвращаемые типы и специализировать аргументы. А это значит, что это не позволит изменять тип свойства.

Даже если компилятор это позволяет, вам, вероятно, не следует этого делать. Безмолвное разрушение системы типов - отличный способ внести ошибки и показатель плохой архитектуры.

49
ответ дан 28 November 2019 в 20:49
поделиться
Другие вопросы по тегам:

Похожие вопросы: