Подтвердить что ты не робот

Точечный синтаксис против синтаксиса метода с геттером =

Я не уверен, как много использовать этот вопрос, но мне кажется интересным...

Я думал, что использование операторов property/synthesize эквивалентно мне, создающему getter/setter. Поэтому

// .h
@property (nonatomic) BOOL on;

// .m
@synthesize on = _on;

// In my mind synthesizes the following methods

// - (BOOL)on;
// - (void)setOn:(BOOL)on;

Однако, если я изменю объявления на следующее:

                              v
@property (nonatomic, getter=isOn) BOOL on;

@synthesize on = _on;

// In my mind synthesizes the following

// - (BOOL)isOn;
// - (void)setOn:(BOOL)on;

Затем, учитывая выше, я переопределяю геттер, чтобы я знал, когда он вызывается:

- (BOOL)isOn;
{
    NSLog(@"I was called");
    return _on;
}

Теперь вызов экземпляра в экземпляре (myClass) приводит к:

NSLog(@"%d", [myClass isOn]);

//=> 2012-02-09 22:18:04.818 Untitled[1569:707] I was called
//=> 2012-02-09 22:18:04.820 Untitled[1569:707] 1

NSLog(@"%d", myClass.isOn);

//=> 2012-02-09 22:18:24.859 Untitled[1599:707] I was called
//=> 2012-02-09 22:18:24.861 Untitled[1599:707] 1

NSLog(@"%d", myClass.on);         // This is the one I didn't expect to work

//=> 2012-02-09 22:18:55.568 Untitled[1629:707] I was called
//=> 2012-02-09 22:18:55.570 Untitled[1629:707] 1

Я всегда предполагал, что если бы я использовал свойство в этом смысле, было бы совершенно справедливо использовать геттер/сеттер с точечным синтаксисом в форме

myClass.isOn;
myClass.on = on;

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

myClass.on   // Correct
myClass.isOn // Incorrect

Хотя это работает, кажется немного менее логичным, потому что я знаю, что нет базового метода - (BOOL)on вместо этого отображается на - (BOOL)isOn

Мои вопросы (с использованием последнего примера)

  • Является ли это ошибкой или должен myClass.on действительно быть тихо изменен для вызова - (BOOL)isOn
  • Семантически говоря, я обращаюсь к состоянию, не вызывающему поведение, так верно ли мое нынешнее использование точечного синтаксиса? (например, myClass.isOn)

Update

Хотя никто прямо не сказал, что я рассуждал, что использование .isOn является плохим, потому что независимо от того, что под капотом вызывается тот же метод, семантически isOn задает вопрос, чем состояние.

Однако я до сих пор неясно, где происходит "волшебная" проводка, которая вызывает вызовы myClass.on в [myClass isOn]


Обновление 2

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

id MyClass = objc_getClass("MyClass");
unsigned int outCount, i;

objc_property_t *properties = class_copyPropertyList(MyClass, &outCount);
for (i = 0; i < outCount; i++) {
    objc_property_t property = properties[i];
    NSLog(@"Name: %s, attributes: %s\n", property_getName(property), property_getAttributes(property));
}

//=> 2012-02-10 07:10:28.333 Untitled[934:707] Name: on, attributes: Tc,GisOn,V_on

Итак, у нас есть следующие атрибуты:

  • name = on
  • type = char (Tc)
  • getter = isOn (GisOn)
  • variable = _on (V_on)

Со всей этой информацией, доступной во время выполнения, возникает вопрос: этот поиск выполняется во время выполнения или время компиляции, как предлагают некоторые ответы?

4b9b3361

Ответ 1

Однако я до сих пор неясно, где происходит "волшебная" проводка, которая вызывает вызовы myClass.on в [myClass isOn]

Логика, конечно же, выглядит следующим образом при компиляции obj.name в контексте получения:

if(there is an accessible @property for name in scope)
{
   if(there is a custom getter specified)
      compile "[obj customGetter]"
   else
      compile "[obj name]"
}
else if (there is an an accessible instance method name in scope)
   compile "[obj name]"
else
{
   compile "[obj name]"
   warn obj may not respond to name
}

Существуют другие способы, с помощью которых язык/среда выполнения может обрабатывать пользовательские имена получателей, но при условии, что Obj-C помещает объявление в заголовок (который является общедоступным), приведенное выше является хорошим предположением о том, где выполняется пользовательская логика геттера - при компиляции сайта вызова.

Ответ 2

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

  • Есть ли свойство с этим именем? Если да, имеет ли оно имя получателя/сеттера? если да, то позвольте назвать этот метод.
  • в противном случае создайте соответствующее имя метода (прямое, если мы получим, setXX, если мы настроим) и выбросим его в приемник.

Вы можете, например, попытаться использовать .count для экземпляра NSArray. Прежде чем мерзость полиции ударит в ваши двери, у вас может быть время, чтобы увидеть, что она работает.

Чтобы на самом деле ответить на ваш вопрос, на мой взгляд, точечная нотация должна использоваться только для доступа к свойствам, и в этом случае вы должны использовать имя свойства, объявленное в интерфейсе. Итак, для UISwitch. Я не знаю, почему имя getter не указано в выражении synhesize, а не в объявлении свойства, оно, кажется, принадлежит к реализации, а не к мне.

Ответ 3

Что касается точечной нотации, позвольте мне привести Aaron Hillegass (Cocoa Программирование для Mac OSX, 3rd Ed):

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

Когда у вас есть переменная-член, а ваш получатель для этой переменной называется isOn, тогда .on и .isOn - два очень разных типа вещей. Используя геттер (и, вероятно, сеттер тоже), вы будете придерживаться обещания "скрывать информацию", а при использовании прямого доступа к переменным-членам вы не будете. Cocoa не будет применять эти вещи, поскольку он полагается на соглашения. Вам решать, какой путь подходит вам. Принимая во внимание соглашение, вам придется придерживаться сеттеров и геттеров - независимо от того, какие имена вы им даете.

Ответ 4

Объявления свойств являются просто сокращением для регулярных объявлений методов. Например:.

@property int color;
@property (getter=isOn) BOOL on;

становится объявлением этого метода:

- (int)color;
- (void)setColor:(int)value;
- (BOOL)isOn;
- (void)setOn:(BOOL)on;

Вы можете вызвать эти методы так же, как и любой другой метод:

[foo color];
[foo isOn];

Аналогичным образом, точечное обозначение является просто неофициальным сокращением для вызова простых старых методов. Например:

x = @"Hello".length;
x = foo.on;
x = foo.isOn;

становится

x = [@"Hello" length];
x = [foo isOn];
x = [foo isOn];

Обратите внимание, что @"Hello".length работает, хотя NSString фактически не объявляет свойство с именем "длина". По умолчанию foo.bar всегда расширяется до [foo bar], если bar не объявлено свойство с пользовательским получателем. Если bar является именем допустимого метода, то он будет работать без ошибок.

Аналогично, в вашем примере foo.isOn работает, даже если вы фактически не объявляете свойство с именем isOn. Скорее "isOn" - это имя метода, который просто является методом getter для вашего свойства "on".

Итак, хотя foo.isOn может работать, он считается плохим, потому что isOn на самом деле не является именем свойства.

Что вы не можете сделать, так это:

x = [foo on]; // Error

потому что вы никогда не объявляете метод on.