Ответ 1
Вам нужно сообщить компилятору, что вы также хотите установить сеттер. Обычный способ заключается в том, чтобы поместить его в расширение в файле .m:
@interface YourClass ()
@property (nonatomic, copy) NSString* eventDomain;
@end
Я объявил свойство readonly в моем интерфейсе как таковом:
@property (readonly, nonatomic, copy) NSString* eventDomain;
Возможно, я неправильно понимаю свойства, но я думал, что когда вы объявляете его как readonly
, вы можете использовать сгенерированный сеттер внутри файла реализации (.m
), но внешние объекты не могут изменить значение. Этот вопрос SO говорит, что должно произойти. Это поведение, которое я переживаю. Однако при попытке использовать стандартный синтаксис сеттера или точки для установки eventDomain
внутри моего метода init он дает мне ошибку unrecognized selector sent to instance.
. Конечно, я @synthesize
свойство. Попытка использовать его следующим образом:
// inside one of my init methods
[self setEventDomain:@"someString"]; // unrecognized selector sent to instance error
Так я не понимаю объявление readonly
для свойства? Или что-то еще происходит?
Вам нужно сообщить компилятору, что вы также хотите установить сеттер. Обычный способ заключается в том, чтобы поместить его в расширение в файле .m:
@interface YourClass ()
@property (nonatomic, copy) NSString* eventDomain;
@end
Другим способом, который я нашел для работы с свойствами readonly, является использование @synthesize для указания хранилища резервных копий. Например
@interface MyClass
@property (readonly) int whatever;
@end
Тогда в реализации
@implementation MyClass
@synthesize whatever = _whatever;
@end
Затем ваши методы могут установить _whatever, поскольку это переменная-член.
Еще одна интересная вещь, которую я осознал в последние несколько дней, - вы можете сделать свойства readonly доступными для записи подклассами вроде:
(в файле заголовка)
@interface MyClass
{
@protected
int _propertyBackingStore;
}
@property (readonly) int myProperty;
@end
Затем в реализации
@synthesize myProperty = _propertyBackingStore;
Он будет использовать объявление в файле заголовка, поэтому подклассы могут обновлять значение свойства, сохраняя при этом свою readonlyness.
К сожалению, с точки зрения скрытия данных и инкапсуляции, хотя.
Эйко и другие дали правильные ответы.
Вот более простой способ: Непосредственный доступ к частной переменной-члену.
Пример
В файле заголовка .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
После объявления свойства foo
вы можете предположить, что Xcode добавил приватную переменную-член, названную с префиксом подчеркивания: _foo
.
Если свойство было объявлено readwrite
, Xcode генерирует метод getter с именем foo
и установщик с именем setFoo
. Эти методы неявно называются при использовании точечной нотации (мой Object.myMethod). Если свойство было объявлено readonly
, сеттер не генерируется. Это означает, что переменная поддержки, названная с подчеркиванием, сама по себе не является readonly. readonly
означает просто, что метод setter не был синтезирован, и поэтому использование точечной нотации для установки значения не выполняется с ошибкой компилятора. Точечное обозначение терпит неудачу, потому что компилятор не позволяет вам вызвать метод (сеттер), который не существует.
Самый простой способ - напрямую обратиться к переменной-члену, названной с подчеркиванием. Вы можете сделать это, даже не объявив эту переменную с подчеркнутым именем! Xcode вставляет это объявление как часть процесса сборки/компиляции, поэтому ваш скомпилированный код действительно будет иметь объявление переменной. Но вы никогда не видите эту декларацию в исходном файле исходного кода. Не магия, просто синтаксический сахар.
Использование self->
- это способ доступа к переменной-члену объекта/экземпляра. Вы можете опустить это и просто использовать имя var. Но я предпочитаю использовать стрелку self +, потому что это делает мой код самодокументированным. Когда вы видите self->_foo
, вы знаете без двусмысленности, что _foo
является переменной-членом в этом экземпляре.
Кстати, обсуждение плюсов и минусов доступа к аксессуарам по сравнению с прямым доступом к ivar - это именно то, что вдумчивое лечение вы прочитаете в Dr. Matt Neuberg Программирование iOS. Мне было очень полезно читать и перечитывать.
См. Настройка существующих классов в Документах iOS.
только для чтения Указывает, что свойство доступно только для чтения. Если вы укажете только для чтения, в @implementation требуется только метод getter. Если вы используете @synthesize в блоке реализации, синтезируется только метод getter. Более того, если вы попытаетесь присвоить значение с помощью синтаксиса точек, вы получите ошибку компилятора.
Свойства Readonly имеют только метод getter. Вы все равно можете установить поддержку ivar непосредственно в классе свойств или с помощью кодирования значения ключа.
Вы недопонимаете другой вопрос. В этом вопросе есть расширение класса, объявленное таким образом:
@interface MYShapeEditorDocument ()
@property (readwrite, copy) NSArray *shapesInOrderBackToFront;
@end
Это то, что генерирует сеттер, только видимый внутри реализации класса. Так, как говорит Эйко, вам нужно объявить расширение класса и переопределить объявление свойства, чтобы сообщить компилятору сгенерировать сеттер только внутри класса.
Самое короткое решение:
MyClass.h
@interface MyClass {
int myProperty;
}
@property (readonly) int myProperty;
@end
MyClass.h
@implementation MyClass
@synthesize myProperty;
@end
Если свойство определено как readonly, это означает, что фактически не будет установщиком, который может использоваться либо внутри класса, либо извне из других классов. (т.е. у вас будет только "getter", если это имеет смысл.)
Из его звуков вы хотите иметь нормальное свойство чтения/записи, помеченное как личное, чего вы можете достичь, установив переменную класса как приватную в свой файл интерфейса как таковой:
@private
NSString* eventDomain;
}