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


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

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

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

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

так я не понимаю readonly декларация на имущество? Или что-то еще происходит?

7 83

7 ответов:

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

@interface YourClass ()

@property (nonatomic, copy) NSString* eventDomain;

@end

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

@interface MyClass

@property (readonly) int whatever;

@end

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

@implementation MyClass

@synthesize whatever = _whatever;

@end

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


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

(в заголовке файл)

@interface MyClass
{
    @protected
    int _propertyBackingStore;
}

@property (readonly) int myProperty;

@end

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

@synthesize myProperty = _propertyBackingStore;

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

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

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

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

пример

в заголовке .H-файл:

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

в реализации .файл м:

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

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

подробности

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

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

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

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

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

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


кстати, обсуждение плюсов и минусов доступа к собственности против прямого Ивара доступ к именно такой вдумчивый лечения, вы будете читать В Д Мэтт Нойберг ' s Программирование iOS книги. Мне было очень полезно читать и перечитывать.

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

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

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

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

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

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

короткий ответ:

MyClass.h

@interface MyClass {

  int myProperty;

}

@property (readonly) int myProperty;

@end

MyClass.h

@implementation MyClass

@synthesize myProperty;

@end

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

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

@private
    NSString* eventDomain;
}