@synthesize 和 @dynamic 的作用分别是什么?
在 Objective-C 中,@synthesize 和 @dynamic 是两个用于配合 @property(属性)使用的编译器指令。它们主要控制存取方法(Getter/Setter)的生成方式以及实例变量(Instance Variable / ivar)的处理。
简单总结:
@synthesize:告诉编译器“帮我生成 Getter/Setter 方法,并关联(或创建)一个实例变量”。@dynamic:告诉编译器“不要生成 Getter/Setter 方法,也不要检查它们是否存在,我会在运行时(Runtime)提供这些方法”。
1. @synthesize (合成)
作用
- 生成访问器方法:如果开发者没有手动实现 Getter 和 Setter,编译器会自动生成它们。
- 生成/绑定实例变量:它会指定属性对应的实例变量(ivar)。如果没有指定,编译器会自动生成一个。
语法
plaintext
// 将属性 name 绑定到实例变量 _name
@synthesize name = _name;
现代 Objective-C 的变化 (Autosynthesis)
在 Xcode 4.4 之后,编译器支持自动合成(Autosynthesis)。这意味着你不再需要显式写 @synthesize。
- 如果你声明了一个属性
@property NSString *name;,编译器默认会自动执行@synthesize name = _name;。 - 它会自动生成
_name变量和对应的 Getter/Setter。
什么时候还需要手动写 @synthesize?
虽然现在很少用,但在以下几种情况编译器不会自动合成,必须手动写:
- 同时重写了 Setter 和 Getter:如果你手动实现了属性的所有访问方法(读写属性同时重写了 set/get,只读属性重写了 get),编译器认为你接管了所有控制权,就不会自动生成 ivar。如果你还需要用到 ivar,必须手动
@synthesize。 - 在 Protocol 中定义的属性:协议里的属性不会自动合成。
- 希望指定非默认的 ivar 名称:例如
@synthesize name = myCustomNameVar;。
2. @dynamic (动态)
作用
- 阻止编译器生成代码:告诉编译器不要自动生成 Getter 和 Setter。
- 阻止编译器生成实例变量:它不会创建
_name这样的成员变量。 - 消除编译警告:告诉编译器“虽然你现在在编译时找不到这两个方法,但请放心,程序运行起来的时候会有办法处理的”,从而消除“Method definition not found”的警告。
核心机制
使用 @dynamic 意味着访问该属性时,方法的实现将推迟到运行时(Runtime)决定。通常涉及以下技术:
- 消息转发(Message Forwarding)。
- 动态方法解析(Dynamic Method Resolution):通过重写
+ (BOOL)resolveInstanceMethod:(SEL)sel在运行时动态添加方法实现。
常见使用场景
- Core Data (
NSManagedObject):
这是最常见的场景。Core Data 的属性值存储在数据库中,而不是对象的实例变量里。NSManagedObject在运行时动态拦截 Getter/Setter 调用来存取数据。因此,Core Data 模型子类的属性通常标记为@dynamic。 - 代理/转发:
当一个类想把属性的存取操作转发给另一个对象处理时。
语法
plaintext
@dynamic name;
总结对比表
| 特性 | @synthesize | @dynamic |
|---|---|---|
| 编译器行为 | 自动生成 Getter/Setter 方法 | 不生成 Getter/Setter 方法 |
| 实例变量 (ivar) | 自动生成或绑定 ivar (如 _name) |
不生成 ivar |
| 运行时风险 | 无(方法编译时已存在) | 有(如果运行时没提供方法,程序会 Crash) |
| 主要用途 | 标准的对象属性存储 | Core Data、动态方法解析、消息转发 |
| 是否必须写 | 现代 Obj-C 中通常省略(默认自动合成) | 必须显式写出,否则编译器会报错或警告 |
代码示例
plaintext
@interface MyClass : NSObject
@property (nonatomic, strong) NSString *synthesizedProp;
@property (nonatomic, strong) NSString *dynamicProp;
@end
@implementation MyClass
// 1. @synthesize
// 现代编译器默认会隐含这一行,生成 _synthesizedProp 变量和 get/set 方法
@synthesize synthesizedProp = _synthesizedProp;
// 2. @dynamic
// 告诉编译器:别管 dynamicProp 的方法,也别生成 _dynamicProp 变量
@dynamic dynamicProp;
- (instancetype)init {
self = [super init];
if (self) {
_synthesizedProp = @"Hello"; // 可以直接访问 ivar
// _dynamicProp = @"World"; // 报错!因为 @dynamic 不会生成 ivar
self.dynamicProp = @"World"; // 编译通过。但如果运行时没处理,会 Crash
}
return self;
}
// 对于 @dynamic 属性,通常需要实现动态解析
+ (BOOL)resolveInstanceMethod:(SEL)sel {
if (sel == @selector(setDynamicProp:) || sel == @selector(dynamicProp)) {
// 在这里动态添加方法实现...
return YES;
}
return [super resolveInstanceMethod:sel];
}
@end