为属性创建自己的setter时的无限递归

Chr*_*ter 4 setter objective-c infinite-loop

我在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;
}
Run Code Online (Sandbox Code Playgroud)

标题AClass.h:

#include <Foundation/Foundation.h>

@interface AClass: NSObject; 

-(void) setAVariable:(int) bVariable;

@property int aVariable;

@end
Run Code Online (Sandbox Code Playgroud)

和相应的实现文件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
Run Code Online (Sandbox Code Playgroud)

在Linux上使用clang或在OSX上使用Xcode编译此代码时,setAVariable:会触发无限递归.我想知道这是否是clang/Objective-C中的错误.

And*_*rew 7

这是预料之中的.您正在访问setter中的setter.

self.aVariable = bVariable实际上是在调用[self setAVariable:bVariable],因此是递归.点语法就是这样,一种特殊的语法,实际上只是实际setter方法的简写.在编写自己的setter方法时,应该访问支持实例变量,而不是属性本身.例如

- (void) setAVariable:(int)bVariable {
  NSLog(@"foo:");
  aVariable = bVariable;  
}
Run Code Online (Sandbox Code Playgroud)

通常的做法是为实例变量使用前导下划线,以便在您直接访问实例变量与属性(通过getter和setter来获取后备实例变量)时很容易识别.

此外,最佳做法是使用#import而不是#include,因为#import只包含一次文件,即使同一文件有多个#import语句,可能会加快编译时间.