1-2.【OC】【基础】@interface 和 @implementation 在编译期和运行时分别承担什么角色?

3 阅读3分钟

在 Objective-C 中,@interface@implementation 的设计完美体现了“C 语言的静态声明”与“Smalltalk 风格的动态响应”的结合。

我们可以从**编译期(Compile Time)运行时(Runtime)**两个阶段来拆解它们扮演的角色:


1. @interface (接口声明)

这一部分主要面向编译器,它类似于 C 语言的 .h 头文件,起到了“契约”的作用。

在编译期(Compiler):

  • 布局确定(Layout): 编译器通过 @interface 中声明的实例变量(ivar)来计算对象的内存布局(Size)。
  • 类型检查: 当你在代码里写 [obj method] 时,编译器会去查看 obj 所属类的 @interface。如果里面没声明这个方法,编译器会报警告或错误(除非你用 id 类型)。
  • 符号映射: 它定义了类的名字、父类、实现的协议以及属性(Property)的访问权限。

在运行时(Runtime):

  • 角色弱化: 一旦程序编译完成,@interface 作为一个文本定义就不再直接存在了。它的信息被固化到了二进制文件的 Read-Only Data 区。
  • 镜像加载: 当 App 启动加载镜像(Image)时,Runtime 会读取这些固化后的类信息,在内存中注册对应的类结构体(objc_class)。

2. @implementation (具体实现)

这一部分既是代码的所在地,也是**消息机制(Messaging)**的弹药库。

在编译期(Compiler):

  • 函数转换: 编译器会将你写的每个方法转换为普通的 C 函数。例如 -(void)doSomething 会变成 void _i_ClassName_doSomething(id self, SEL _cmd)
  • 属性合成: 编译器会自动生成 gettersetter 的代码,以及对应的底层变量(如果你没有手动 @synthesize)。

在运行时(Runtime):核心舞台

这是 Smalltalk 风格消息机制发挥作用的地方:

  • 方法列表(Method List)注册: 当类被加载到内存时,@implementation 中写的方法会被登记在该类的 Method List 中。每个条目包含:SEL(方法名/选择子)和 IMP(转换后的 C 函数指针)。
  • 动态绑定: 当发送消息 [obj doSomething] 时,Runtime 完全不管编译期是怎么声明的,它直接去类的方法列表里通过 SELIMP
  • 动态修改: 既然 @implementation 的结果是运行时的一个列表,你就可以在程序跑起来后,通过 class_addMethodmethod_exchangeImplementations (Method Swizzling) 动态地替换或增加实现。

总结对比

阶段@interface 的角色@implementation 的角色
编译期合同工:告诉编译器对象长什么样,有哪些方法可以叫,不符合就报错。加工厂:把逻辑代码编译成 C 函数,准备好供 Runtime 调用的数据结构。
运行时静态参考:提供类名、父类等基础元数据。动态执行:提供方法列表映射。消息传递的核心就是根据 SEL 在这个实现列表中找代码地址。

深度理解:为什么说它是 Smalltalk 风格?

在 C++ 中,如果你在类里增加一个方法,必须重新编译所有引用该类的代码,因为函数偏移量在编译期就定死了。

而在 Objective-C 中,@interface 只是个“暗示”,真正的权力在 @implementation 加载到 Runtime 后的那个方法映射表里。你可以只给一个对象发消息,而不关心它是否在 @interface 里声明过(通过 performSelector:),只要 @implementation 里有,或者运行时动态塞进去了,程序就能跑通。这就是 C 的形式 + Smalltalk 的内核