Бесконечная рекурсия при создании собственного сеттера для свойства

Я наткнулся на какое-то странное поведение в Objective-C. У меня есть main.m:

#include <Foundation/Foundation.h>
#include "AClass.h"

int main(int argc, char* argv[]) {
  AClass* tmpClass = [[AClass alloc] init];
  [tmpClass setAVariable:12];
  return -1;
}

Заголовок AClass.h:

#include <Foundation/Foundation.h>

@interface AClass: NSObject; 

-(void) setAVariable:(int) bVariable;

@property int aVariable;

@end

и соответствующий файл реализации AClass.m:

#include <Foundation/Foundation.h>
#include <AClass.h>

@implementation AClass
@dynamic aVariable;
int aVariable;

-(void) setAVariable:(int)bVariable {
  NSLog(@"foo:");
  self.aVariable = bVariable;  
}

@end

При компиляции этого кода с помощью clang в Linux или через Xcode в OSX setAVariable: запускает бесконечную рекурсию. Интересно, это ошибка в clang/Objective-C.


person Christian Richter    schedule 17.04.2013    source источник
comment
Вы должны знать, что ваше объявление aVariable предназначено для глобального, а не ivar, как вы, кажется, ожидаете. Он должен быть заключен в фигурные скобки вверху блока @implementation: @implementation AClass { int aVariable; } /* etc. */ @end   -  person jscs    schedule 17.04.2013
comment
Это было моим намерением :)   -  person Christian Richter    schedule 18.04.2013


Ответы (1)


Это ожидаемо. Вы получаете доступ к своему сеттеру внутри вашего сеттера.

self.aVariable = bVariable на самом деле вызывает [self setAVariable:bVariable], отсюда и рекурсия. Точечный синтаксис — это всего лишь специальный синтаксис, который на самом деле является просто сокращением для фактического метода установки. Когда вы пишете свой собственный метод установки, вы должны обращаться к переменной резервного экземпляра, а не к самому свойству. Например.

- (void) setAVariable:(int)bVariable {
  NSLog(@"foo:");
  aVariable = bVariable;  
}

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

Кроме того, рекомендуется использовать #import вместо #include, поскольку #import включает файл только один раз, даже если для одного и того же файла имеется несколько операторов #import, что может ускорить время компиляции.

person Andrew    schedule 17.04.2013