Наследование свойств, сеттер не синтезируется при перезаписи в унаследованном свойстве от readonly
Я обнаружил странное поведение при работе со свойством, которое было унаследовано как readonly, а затем повторно объявлено в унаследованном классе как readwrite.
В ах
@interface A : NSObject
@property (nonatomic, strong, readonly) NSObject * someProperty;
@end
В ч
@interface B : A
// no matter if here
// @property (nonatomic, strong, readwrite) NSObject * someProperty;
- (void)foo;
@end
В Бм
@interface B()
// no matter if here
@property (nonatomic, strong, readwrite) NSObject * someProperty;
@end
@implementation B
- (void)foo {
NSLog(@"%@", self.someProperty);
// crash here with unrecognized selector setSomeProperty:
self.someProperty = [NSObject new];
}
@end
призвание
self.someProperty = [NSObject new];
вызывает сбой кода на нераспознанном селекторе "setSomeProperty:"
Расследование показало, что похоже, что сеттер не был синтезирован, даже если он объявлен как readwrite
Почему это происходит? Компилятор не указал ни одного предупреждения, чтобы это произошло, и я нигде не обнаружил, что это поведение задокументировано
2 ответа
Добавить @synthesize
директива к файлу Bm и сбой исчезнет:
@synthesize someProperty = _someProperty;
Проблема в том, что в родительском классе вы объявили свойство как readonly
для него нет синтезатора. И подклассы наследуют это поведение. Даже если вы переименуете свойство в readwrite
в подклассе. @synthesize
Команда даст указание компилятору снова сгенерировать методы доступа для класса B.
Надеюсь это поможет!
Я не могу дать вам официальную ссылку, но вот что я испытал: для свойства, унаследованного от суперкласса, компилятор не генерирует никаких методов доступа.
В вашем случае недвижимость объявляется как readonly
в классе А, так что компилятор создает только метод получения. Повторное объявление в классе B не создает никаких дополнительных методов доступа. Причиной может быть то, что класс B не знает, "как" свойство реализовано в классе A (оно не обязательно должно быть переменной экземпляра).
Таким образом, объявление свойства в подклассе является лишь "обещанием" компилятору, что функции getter и setter будут доступны во время выполнения (аналогично объявлению @dynamic). Если нет установщика, вы получите исключение во время выполнения.
Таким образом, вариант использования повторного объявления свойства в подклассе будет, если суперкласс объявит свойство как доступное только для чтения в общедоступном интерфейсе, но как чтение-запись в (приватном) расширении класса:
// A.h
@interface A : NSObject
@property (nonatomic, strong, readonly) NSObject * someProperty;
@end
// A.m
@interface A()
@property (nonatomic, strong, readwrite) NSObject * someProperty;
@end
@implementation A
@end
В этом случае и метод set, и метод get создаются в классе A, а класс B может повторно объявить свойство как чтение-запись в своей реализации.