objc 推荐我们通过 set/get 方法访问对象的属性。很显然,为每一个属性手动添加 set/get 方法的声明和实现是个性价比很低的重复劳动。因此,objc 提供了一些关键字帮助我们简化这一过程。实际上就是这么回事儿。
@property
首先来看现在的 property:
例如:
@interface ViewController : UIViewController
@property (nonatomic,assign) BOOL testVar;
@end
简单理解,相当于声明了成员变量_testVar
,声明了实现了 set 方法setTestVar
、get 方法testVar
,等价于:
@interface ViewController : UIViewController
{BOOL _testVar;}
- (void)setTestVar:(BOOL)newTestVar;
- (BOOL)testVar;
@end
@implementation ViewController
- (void)setTestVar:(BOOL)newTestVar
{_testVar = newTestVar;}
- (BOOL)testVar
{return _testVar;}
@end
@synthesize
@synthesize 可以指定 set/get 方法的实现。
1. 默认
默认地,@synthesize 会生成一个同名的成员变量作为 set/get 的目标。
例如:
@interface ViewController : UIViewController
@property (nonatomic,assign) BOOL testVar;
@end
@implementation ViewController
@synthesize testVar;
@end
等价于:
@interface ViewController : UIViewController
{BOOL testVar;}
- (void)setTestVar:(BOOL)newTestVar;
- (BOOL)testVar;
@end
@implementation ViewController
- (void)setTestVar:(BOOL)newTestVar
{testVar = newTestVar;}
- (BOOL)testVar
{return testVar;}
@end
可以看到,set/get 方法指向了成员变量testVar
,也不会再默认生成_testVar
。
注意,很多人误认为 @synthesize 默认生成的成员变量是_testVar
,据我所知这是不对的。
官方文档:
Important: If you use
@synthesize
without specifying an instance variable name, like this:@synthesize firstName;
the instance variable will bear the same name as the property.
In this example, the instance variable will also be called
firstName
, without an underscore.
2. 指定
@synthesize 也可以指定一个成员变量作为其 set/get 的目标。
例如:
@interface ViewController : UIViewController
{BOOL exampleVar;}
@property (nonatomic,assign) BOOL testVar;
@end
@implementation ViewController
@synthesize testVar = exampleVar;
@end
等价于:
@interface ViewController : UIViewController
{BOOL exampleVar;}
- (void)setTestVar:(BOOL)newTestVar;
- (BOOL)testVar;
@end
@implementation ViewController
- (void)setTestVar:(BOOL)newTestVar
{exampleVar = newTestVar;}
- (BOOL)testVar
{return exampleVar;}
@end
此时 set/get 方法就指向了成员变量exampleVar
。
特别的,可以声明并指定成员变量_testVar
,例如:
@interface ViewController : UIViewController
{BOOL _testVar;}
@property (nonatomic,assign) BOOL testVar;
@end
@implementation ViewController
@synthesize testVar = _testVar;
@end
这是最符合官方规范的做法:每个属性对应一个以 _
开头的成员变量。
可以看出,这种写法跟本文第一部分,只写一个 @property
的作用是一样的。
一点历史
其实,在 xcode4.4(对应 clang3.2/LLVM4.0)以前,显式声明 _testVar
和@synthesize testVar = _testVar;
是标准做法。那时候只写个 @property
并不会给你带来成员变量和 set/get 的实现。
现在的表现是编译器把以前的标准做法化作默认行为的结果。当我们只声明了 @property
而没有实现对应的 set/get 方法时,编译器会默认声明 _
开头的成员变量并补全 set/get 的实现。
一个真实案例
最近遇到的问题是一坨老代码,里面用了 @synthesize
但是似乎不是很规范,后来维护的人看起来也不太熟悉这几个关键字,照着前人的代码瞎写,最后代码类似下面的:
@interface ViewController : UIViewController
{BOOL _testVar;}
@property (nonatomic,assign) BOOL testVar;
@end
@implementation ViewController
@synthesize testVar;
- (void)viewDidLoad {[super viewDidLoad];
self.testVar = YES;
if(_testVar){[self doSomething];
}
}
@end
可以看到,代码逻辑里用 _testVar
去做条件判断其实是完全无效的。
@dynamic
@dynamic 的作用是告诉编译器,此属性对应的 set/get 方法将会被动态提供。
说得明白一点,其实 @dynamic 做了两件事:
- set/get 我会自己提供的,不用帮我生成了
- 我会动态提供,没直接写在这里,编译器别给我报错
通常如果用到 dynamic 的话,这些方法会在消息转发的时候处理,也可以通过 runtime 的一些机制动态插入。这里有一些例子。
一般开发中基本上不会用到。