Свойства только для чтения в Objective-C? - PullRequest
87 голосов
/ 03 января 2011

Я объявил свойство только для чтения в своем интерфейсе следующим образом:

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

Возможно, я неправильно понимаю свойства, но я подумал, что когда вы объявляете его как readonly, вы можете использовать сгенерированный установщиквнутри файла реализации (.m), но внешние сущности не могут изменить значение. Этот ТАК вопрос говорит, что так и должно быть.Это поведение, которое я после.Однако, когда я пытаюсь использовать стандартный установщик или синтаксис точки для установки 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 ]

109 голосов
/ 03 января 2011

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

@interface YourClass ()

@property (nonatomic, copy) NSString* eventDomain;

@end
35 голосов
/ 30 мая 2012

Другой способ работы со свойствами, доступными только для чтения, - это использование @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;

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

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

34 голосов
/ 14 августа 2013

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

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

Пример

В шапке .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 является переменной-членом в этом экземпляре.


Кстати, обсуждение плюсов и минусов средств доступа к свойствам в сравнении с прямым доступом к ивару - это именно та забота, которую вы прочтете в докторе Мэтт Нойберг 1063 * Программирование iOS книга. Мне было очень полезно читать и перечитывать.

20 голосов
/ 03 января 2011

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

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

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

7 голосов
/ 03 января 2011

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

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

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

5 голосов
/ 03 апреля 2013

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

MyClass.h

@interface MyClass {

  int myProperty;

}

@property (readonly) int myProperty;

@end

MyClass.h

@implementation MyClass

@synthesize myProperty;

@end
2 голосов
/ 03 января 2011

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

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

@private
    NSString* eventDomain;
}
...