Clang 编译器提供了 OC 自动合成属性的功能。如果一个属性没有被声明为 @dynamic
或者开发者没有自定制它的 getter
或者 setter
方法实现。那 Clang
会自动为你合成 getter
和 setter
方法的实现同时生成对应的成员变量,检查 Clang
编译器是否支持自动合成使用__has_feature(objc_default_synthesize_properties)
这个条件判断。举例
#if __has_feature(objc_default_synthesize_properties)
//support autosynthesis
#else
//not support
#endif
当编译器不能自动为你合成属性的时候,需要开发者手动进行。其实也可以换个说法,什么时候需要开发者手动 @synthesize
。
举个例子,开发中偶尔会遇到这种报错的情况:
@property (nonatomic,strong) NSString *name;
.....
@implementation
- (void)setName:(NSString *)name {
_name = name; #编译错误:Use of undeclared identifier '_name'; did you mean 'name'?
}
- (NSString *)name {
return @"";
}
@end
这个时候需要我们手动的添加一行代码 @synthesize name = _name;
编译错误就会消失。或者你把编译错误的那行去掉,然后使用命令 clang -rewrite-objc main.m
编译原文件,发现生成的对象结构体中并没有 name
对应的成员变量。
接下来就是总结什么场景下需要我们手动添加 @synthesize
。
- 就是我们上面
demo
展示的这种可读写的属性,但是开发者自定义了getter
和setter
方法,注意必须是同时复写,如果只复写setter
或者getter
的话是不会出现问题的。这也是clang
编译器不支持的自动合成的场景。 - 只读属性,开发者自定义了
getter
方法。 @dynamic
修饰的属性,@dynamic
本质是告诉编译器setter
与getter
方法由开发者自己定义,不自动合成。- 在协议
@protocol
中声明的属性。 - 在类别
@category
中声明的属性。这是因为类别不支持自动添加成员变量,需要手动进行引用关联。 - 如果你复写了父类的属性,你也需要显式地添加
@synthesize
- 如果你想手动修改成员变量名的话,可以使用
@synthesize
修改,比如默认成员变量名是_name
使用@synthesize name = yname;
修改成员变量名为yname
,但是不建议这么做。
2018-03-23
补充一些关于第 6 点的说明。
如果父类自动生成属性成员变量和 getter, setter 方法。子类对父类的属性重新进行了 @synthesize
那将会生成新的实例变量。
@interface Fan : NSObject
@property (nonatomic, strong) NSString *name;
@end
//父类 Fan 并没有 @synthesize 而且没有复写 getter 和 setter 方法.
......
@interface FanSub : Fan
@property (nonatomic, strong) NSString *name;
....
@implementation FanSub
@synthesize name = fname;
- (void)setName:(NSString *)name {
fname = name;
}
- (NSString *)name {
return fname;
}
@end
注意的是此时子类中的 name
属性对应的实例变量其实是 fname
. 即它自己生成的实例变量,而不是父类的 _name
了。可以通过查看编译后的选项来验证 clang -rewrite-objc main.m
。结合编译后对应的结构体看一下,确实是成员变量发生了变化。所以这个时候要注意,不能把子类的属性成员变量和父类的成员变量混为一谈。
//编译后的结构体.
struct Fan_IMPL {
struct NSObject_IMPL NSObject_IVARS;
NSString *_name;
};
struct FanSub_IMPL {
struct Fan_IMPL Fan_IVARS;
NSString *fname;
};
一点总结:
所以子类合成和使用属性对应自动变量的两个原则:
- 不允许合成和父类名字一样自动变量
- 如果在子类中有用到父类属性对应的成动变量,则需要显式地在父类头文件的成员变量区域去进行自动变量的声明。
# 一些 QA
Q:@synthesize 合成实例变量的规则是什么?假如 property 名为 foo,存在一个名为_foo 的实例变量,那么还会自动合成新变量么? A: 默认合成带属性名字前面带下划线的成员变量。如果这个下划线的成员变量已经存在,就不会合成新的成员变量了。即上面问题答案是不会合成新的成员变量了。
#参考地址#