Переопределение объявлений @property в Objective-C - PullRequest
24 голосов
/ 17 августа 2011

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

Редактировать: Изменение @synthesize на @dynamic в Base имеет больше смысла.

Редактировать: Для этого требуется iOS SDK 5.

Ответы [ 2 ]

48 голосов
/ 17 августа 2011

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

Типы возврата

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

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

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

1 голос
/ 17 августа 2011

Вы на самом деле не можете этого сделать.Для меня я получаю ошибку

property 'obj' attempting to use ivar 'obj_' declared in super class of 'Derived'

, поэтому я думаю, что это очевидно.И даже если вы не используете @synthesize и не определяете функции самостоятельно, то вызывается только производная версия (в ObjC нет такой вещи, как перегрузка функций или виртуальные функции).

Вы должны пересмотреть дизайн своего класса.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...