Наследование свойств, сеттер не синтезируется при перезаписи в унаследованном свойстве от 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 может повторно объявить свойство как чтение-запись в своей реализации.

Другие вопросы по тегам