Swift 的 extension 虽然在命名上承袭了 Objective-C,但其底层逻辑更接近于“编译时静态能力的注入”,且在类型安全和协议扩展方面远比 Objective-C 强大。
我们可以通过“结构化能力”和“编译器限制”两个维度来拆解:
1. Swift Extension 可以做什么?
在 Swift 中,扩展是功能增强的“万能胶水”:
-
添加计算型属性 (Computed Properties) :
可以添加
static或实例级别的计算型属性。 -
添加方法 (Methods) :
包括实例方法、类型方法,以及在
struct/enum中使用mutating关键字修改自身的方法。 -
添加初始化器 (Initializers) :
- 对类 (Class) :只能添加 便利初始化器 (convenience initializer) ,不能添加指定初始化器 (designated initializer)。
- 对值类型 (Struct) :如果原定义中没有写构造函数,在扩展里写构造函数可以保留系统默认的逐一成员构造器(Memberwise Initializer)。
-
遵循协议 (Protocol Conformance) :
这是 Swift 开发最推荐的模式,将协议实现逻辑分离到扩展中,保持主代码结构清晰。
-
添加下标 (Subscripts) :
为现有类型增加新的索引访问方式。
-
嵌套类型 (Nested Types) :
可以在扩展中定义新的
enum或struct。 -
协议扩展 (Protocol Extension) —— Swift 的杀手锏:
这是 Swift “面向协议编程”的核心。你可以为协议提供默认实现,这样遵循该协议的所有类型都会自动获得这些行为。
2. Swift Extension 不能做什么?
由于 Swift 是一种静态语言,它对内存布局和继承链有极其严格的保护:
-
不能添加存储型属性 (Stored Properties) :
扩展不能改变实例的内存大小和布局。这意味着你不能在
extension里直接写var x = 0。 -
不能添加属性观察器 (Property Observers) :
你不能在扩展中为已有的属性添加
willSet或didSet。 -
不能重写 (Override) 已有的行为:
Swift 的扩展原则上不能重写类型中已存在的方法。重写必须在子类中进行。
特例: 如果是在 Objective-C 环境下(标注了
@objc),可以通过一些特殊手段实现,但这不是纯 Swift 的行为。 -
不能添加指定初始化器 (Designated Initializers) 给类:
类的主构造函数必须在类定义中,以确保初始化链的完整性和内存分配的正确性。
-
不能在 Extension 里声明已有的属性:
不能在扩展中重新声明一个已经在主定义中存在的变量名。
3. Swift Extension 与 OC Category 的本质区别
虽然外表相似,但底层的“性格”截然不同:
| 特性 | Swift Extension | Objective-C Category |
|---|---|---|
| 方法查找 | 静态派发 (除泛型/协议外) | 动态派发 (runtime) |
| 命名冲突 | 编译器会根据 Module 区分,冲突概率低 | 运行时“覆盖”原类,容易冲突 |
| 协议增强 | 支持默认实现,威力巨大 | 仅能声明遵循,无法提供默认逻辑 |
| 关联对象 | 需要手动桥接 objc_setAssociatedObject | 天生支持运行时关联 |
4. 独特应用:带条件的扩展 (Conditional Extensions)
这是 Swift 最优雅的特性之一,你可以根据泛型约束来决定扩展是否生效:
Swift
// 只有当数组里的元素也是可比较(Comparable)的时候,Array 才会获得这个方法
extension Array where Element: Comparable {
func sortedDescending() -> [Element] {
return self.sorted(by: >)
}
}
💡 核心避坑指南
- 关联对象模拟存储属性:如果你必须在扩展中存储数据,只能通过 Objective-C Runtime 的
objc_get/setAssociatedObject来“挂载”变量。 - 代码组织:建议将
UITableViewDelegate等协议实现放在单独的extension中,并加上// MARK: -注释,这是业界标准的最佳实践。