it-swarm.xyz

Свойства только для чтения в Objective-C?

Я объявил свойство readonly в моем интерфейсе как таковое:

 @property (readonly, nonatomic, copy) NSString* eventDomain;

Возможно, я неправильно понимаю свойства, но я подумал, что когда вы объявляете его как readonly, вы можете использовать сгенерированный установщик внутри файла реализации (.m), но внешние сущности не могут изменить значение. Этот SO вопрос говорит, что так и должно быть. Это поведение, которое я после. Однако, когда я пытаюсь использовать стандартный синтаксис set или dot для установки eventDomain внутри моего метода init, это вызывает ошибку unrecognized selector sent to instance.. Конечно, я @synthesizeing собственности. Попытка использовать это так:

 // inside one of my init methods
 [self setEventDomain:@"someString"]; // unrecognized selector sent to instance error

Итак, я неправильно понимаю объявление readonly для свойства? Или что-то еще происходит?

87
Alex

Вы должны сообщить компилятору, что вам также нужен установщик. Обычный способ - поместить его в расширение класса в файле .m:

@interface YourClass ()

@property (nonatomic, copy) NSString* eventDomain;

@end
109
Eiko

Другой способ работы со свойствами, доступными только для чтения, заключается в использовании @synthesize для указания резервного хранилища. Например

@interface MyClass

@property (readonly) int whatever;

@end

Тогда в реализации

@implementation MyClass

@synthesize whatever = _whatever;

@end

Ваши методы могут затем установить _whither, так как это переменная-член.


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

(в заголовочном файле)

@interface MyClass
{
    @protected
    int _propertyBackingStore;
}

@property (readonly) int myProperty;

@end

Затем в реализации

@synthesize myProperty = _propertyBackingStore;

Он будет использовать объявление в заголовочном файле, поэтому подклассы могут обновлять значение свойства, сохраняя его только для чтения.

Немного прискорбно с точки зрения сокрытия данных и инкапсуляции.

35
yano

Эйко и другие дали правильные ответы.

Вот более простой способ: Прямой доступ к закрытой переменной-члену.

Пример

В заголовочном файле .h:

@property (strong, nonatomic, readonly) NSString* foo;

В файле реализации .m:

// inside one of my init methods
self->_foo = @"someString"; // Notice the underscore prefix of var name.

Вот и все, это все, что вам нужно. Нет суеты, нет суеты.

Подробности

Начиная с Xcode 4.4 и LLVM Compiler 4.0 ( Новые функции в Xcode 4.4 ) вам не нужно возиться с рутинной работой, обсуждаемой в других ответах:

  • Ключевое слово synthesize
  • Объявление переменной
  • Повторное объявление свойства в файле .m реализации.

После объявления свойства foo можно предположить, что Xcode добавил закрытую переменную-член с префиксом подчеркивания: _foo.

Если свойство было объявлено readwrite, Xcode генерирует метод получения с именем foo и метод установки с именем setFoo. Эти методы неявно вызываются при использовании точечной нотации (мой Object.myMethod). Если свойство было объявлено readonly, сеттер не генерируется. Это означает, что вспомогательная переменная, названная подчеркиванием, сама по себе не только для чтения. readonly просто означает, что метод установки не был синтезирован, и поэтому использование точечной нотации для установки значения завершается с ошибкой компилятора. Точечная нотация завершается ошибкой, потому что компилятор мешает вам вызвать метод (установщик), который не существует.

Самый простой способ обойти это - получить прямой доступ к переменной-члену, названной подчеркиванием. Вы можете сделать это даже без объявления этой переменной с подчеркиванием! Xcode вставляет это объявление как часть процесса сборки/компиляции, поэтому ваш скомпилированный код действительно будет иметь объявление переменной. Но вы никогда не увидите это объявление в исходном файле исходного кода. Не волшебство, просто синтаксический сахар .

Использование self-> - это способ доступа к переменной-члену объекта/экземпляра. Вы можете пропустить это и просто использовать имя переменной. Но я предпочитаю использовать стрелку self +, потому что это делает мой код самодокументированным. Когда вы видите self->_foo, вы без сомнения понимаете, что _foo является переменной-членом в этом экземпляре.


Кстати, обсуждение плюсов и минусов средств доступа к собственности по сравнению с прямым доступом к ивару - это именно та заботливая обработка, которую вы прочтете в Dr. Matt Neuberg 's Программирование iOS книга. Мне было очень полезно читать и перечитывать.

34
Basil Bourque

См. Настройка существующих классов в Документах iOS.

readonly Указывает, что свойство доступно только для чтения. Если вы укажете только для чтения, в @implementation требуется только метод получения. Если вы используете @synthesize в блоке реализации, синтезируется только метод getter. Более того, если вы попытаетесь присвоить значение, используя точечный синтаксис, вы получите ошибку компилятора.

Свойства только для чтения имеют только метод получения. Вы все еще можете установить резервный ivar непосредственно в классе свойства или с помощью кодирования значения ключа.

20
Jonah

Вы неправильно понимаете другой вопрос. в этом вопросе есть расширение класса, объявленное так:

@interface MYShapeEditorDocument ()
@property (readwrite, copy) NSArray *shapesInOrderBackToFront;
@end

Это то, что генерирует сеттер, видимый только в реализации класса. Итак, как говорит Эйко, вам нужно объявить расширение класса и переопределить объявление свойства, чтобы сказать компилятору генерировать сеттер только внутри класса.

7
BoltClock

Самое короткое решение:

MyClass.h

@interface MyClass {

  int myProperty;

}

@property (readonly) int myProperty;

@end

MyClass.h

@implementation MyClass

@synthesize myProperty;

@end
5
user2159978

Если свойство определено как доступное только для чтения, это означает, что фактически не будет установщика, который можно использовать как внутри класса, так и извне из других классов. (т.е. у вас будет "добытчик", только если это имеет смысл.)

Судя по всему, вам нужно обычное свойство чтения/записи, помеченное как приватное, чего можно добиться, установив переменную класса как приватную в вашем файле интерфейса следующим образом:

@private
    NSString* eventDomain;
}
2
John Parker