在 Objective-C 中,Protocol(协议)定义了一组方法规范。@required 和 @optional 关键字决定了遵循该协议的类**“必须实现”还是“可以选择实现”**这些方法。
两者的核心区别在于 编译器的检查强度 和 运行时的安全处理。
1. @required (默认)
如果你不显式指定,协议中的所有方法默认都是 @required。
- 编译期约束:如果一个类声明遵循了协议,但没有实现其中的
@required方法,编译器会抛出 Warning(警告)。虽然不会导致编译失败,但这是强烈的风险提示。 - 用途:用于定义协议的核心逻辑。没有这些方法,协议的功能就无法闭环。例如
UITableViewDataSource中的tableView:cellForRowAtIndexPath:。
2. @optional
使用 @optional 关键字后,其下方定义的方法直到遇到下一个关键字前都是可选的。
- 编译期约束:类可以选择不实现这些方法,编译器不会报任何警告。
- 用途:用于定义扩展功能或钩子(Hooks) 。例如
UITableViewDelegate中的各种点击回调,如果你不关心点击事件,就不必实现。
3. 运行时的“安全陷阱”
这是两者在开发中最本质的区别:调用方式不同。
调用 @required 方法
由于编译器保证了实现(理论上),你可以直接调用:
Objective-C
[self.delegate mustDoSomething];
调用 @optional 方法(必须检查!)
你绝对不能直接调用 @optional 方法。如果委托方(Delegate)没有实现该方法,直接调用会导致 unrecognized selector sent to instance 崩溃。
标准做法: 在调用前使用 respondsToSelector: 进行检查。
Objective-C
if ([self.delegate respondsToSelector:@selector(optionalMethod)]) {
[self.delegate optionalMethod];
}
4. 底层实现差异
在 Runtime 层面,协议被存储为 protocol_t 结构体,它内部维护了四个不同的方法列表:
- Instance Methods (Required)
- Instance Methods (Optional)
- Class Methods (Required)
- Class Methods (Optional)
当你为一个类动态添加协议或者在运行时查询协议时,Runtime 会根据这些分类来区分哪些方法是必须校验的。
5. 总结对比
| 特性 | @required | @optional |
|---|---|---|
| 默认状态 | 是 | 否 |
| 编译器警告 | 未实现时报 Warning | 不报警告 |
| 调用安全性 | 相对安全(依赖警告修复) | 危险,必须手动检查 |
| 主要场景 | 数据源(DataSource)、核心回调 | 配置项、UI 样式定制、非必须回调 |
💡 深度启发:性能优化
在一些高性能场景下(如 UITableView 频繁滑动),如果每次都调 respondsToSelector: 会有微小的性能损耗。资深的开发者通常会在 setDelegate: 时,通过**位运算(Bitfield)**预先缓存该 Delegate 实现了哪些可选方法。