Подтвердить что ты не робот

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

Я часто обнаруживаю, что знаю, что определенное свойство базового класса всегда будет определенным типом в подклассе. Например, в приведенном ниже примере свойство obj всегда будет объектом NSString в Derived. Однако мне нужно, чтобы это свойство было более общим типом id в классе 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 в базе имеет больше смысла.

Изменить: Для этого требуется IOS SDK 5.

4b9b3361

Ответ 1

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

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

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

@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* s. И таким образом я нарушил систему типов.

Свойства

Вот клеймо. Когда вы объявляете тип свойства, вы объявляете как возвращаемый тип getter, так и тип аргумента setter. Согласно приведенным выше правилам это означает, что вы не можете изменить тип свойства, потому что в одном из двух случаев вы будете нарушать систему типов.


Выше была теория. На практике я понятия не имею, если GCC или Clang применяют эти ограничения. Возможно, они предполагают, что программист знает лучше всего, и ненадлежащим образом обобщая или специализируя тип, будет тихо нарушать систему типов за вашей спиной. Вам придется экспериментировать. Но если компилятор действительно прав, он будет запрещать обобщение возвращаемых типов и специализированных аргументов. И это означает, что он запретит изменение типа свойства.

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

Ответ 2

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

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

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

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