1-6.【OC】【基础】@property 是语言特性,还是编译器特性?

0 阅读2分钟

@property 本质上是一个“编译器特性”,但它必须依赖“运行时(Runtime)”的支持才能闭环。

如果用一句话概括: @property 是编译器帮你写的“代码模版”。


1. 为什么说是编译器特性?

在早期的 Objective-C 中,你需要手动声明成员变量(ivar),并手写 gettersetter 方法。这非常枯燥。

当你写下 @property (nonatomic, copy) NSString *name; 时,编译器(Clang)会自动为你完成以下三件事(即 Auto-synthesize):

  1. 生成成员变量: 自动在类中创建一个带下划线的变量 _name
  2. 生成声明:@interface 中声明 -(NSString *)name-(void)setName:(NSString *)name
  3. 生成实现:@implementation 中根据你的参数(copy, nonatomic)自动填充方法内部的逻辑。

证据: 如果你反编译代码,你会发现 @property 消失了,取而代之的是标准的 C 函数和 ivar 访问逻辑。这证明了它主要是在编译阶段进行的“语法糖”展开。


2. 为什么它离不开运行时(Runtime)?

虽然代码是编译器写的,但这些属性的**元数据(Metadata)**会被保存在二进制文件中,并在运行时由 Runtime 接管:

  • 内省(Introspection): 你可以在运行时通过 class_copyPropertyList 获取一个类所有的属性名和特性(Attributes)。
  • KVC 访问: 当你调用 setValue:forKey: 时,Runtime 会根据属性名动态查找对应的 setter
  • 动态绑定: 属性的访问本质上还是发送消息。如果没有 Runtime 的消息分发,编译器生成的这些方法就无法被调用。

3. 核心参数的背后:编译器做了什么?

编译器会根据你括号里的参数,生成完全不同的代码逻辑:

参数编译器生成的伪代码(Setter 内部)目的
assign_value = newValue;简单的赋值,用于基本类型。
retain[newValue retain]; [_value release]; _value = newValue;内存管理,增加引用计数。
copy_value = [newValue copy];防御性拷贝,防止外部修改原对象。
atomicos_unfair_lock_lock(...); _value = newValue; ...自动加锁,保证赋值操作的原子性。

4. 特殊情况:如果你不想用编译器的自动化

有时候你并不想让编译器“代笔”,这时你会用到两个指令:

  • @synthesize:虽然现在默认开启,但你可以用它来给变量改名,比如 @synthesize name = realName;
  • @dynamic这是理解“运行时”的关键。 它告诉编译器:“不要帮我生成方法实现,也不要报错,我保证在运行时(或者通过手动手写)会有这个方法。”

总结

@property 是一个编译期指令,它极大地简化了符合 Cocoa 编程规范(即使用特定的方法名读写变量)的代码编写工作。但它的存在意义是为了支撑 Objective-C 那套高度动态的运行环境