10-3.【OC】【extension】Swift extension 可以做什么?不能做什么?

4 阅读3分钟

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)

    可以在扩展中定义新的 enumstruct

  • 协议扩展 (Protocol Extension) —— Swift 的杀手锏

    这是 Swift “面向协议编程”的核心。你可以为协议提供默认实现,这样遵循该协议的所有类型都会自动获得这些行为。


2. Swift Extension 不能做什么?

由于 Swift 是一种静态语言,它对内存布局和继承链有极其严格的保护:

  • 不能添加存储型属性 (Stored Properties)

    扩展不能改变实例的内存大小和布局。这意味着你不能在 extension 里直接写 var x = 0

  • 不能添加属性观察器 (Property Observers)

    你不能在扩展中为已有的属性添加 willSetdidSet

  • 不能重写 (Override) 已有的行为

    Swift 的扩展原则上不能重写类型中已存在的方法。重写必须在子类中进行。

    特例: 如果是在 Objective-C 环境下(标注了 @objc),可以通过一些特殊手段实现,但这不是纯 Swift 的行为。

  • 不能添加指定初始化器 (Designated Initializers) 给类

    类的主构造函数必须在类定义中,以确保初始化链的完整性和内存分配的正确性。

  • 不能在 Extension 里声明已有的属性

    不能在扩展中重新声明一个已经在主定义中存在的变量名。


3. Swift Extension 与 OC Category 的本质区别

虽然外表相似,但底层的“性格”截然不同:

特性Swift ExtensionObjective-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: >)
    }
}

💡 核心避坑指南

  1. 关联对象模拟存储属性:如果你必须在扩展中存储数据,只能通过 Objective-C Runtime 的 objc_get/setAssociatedObject 来“挂载”变量。
  2. 代码组织:建议将 UITableViewDelegate 等协议实现放在单独的 extension 中,并加上 // MARK: - 注释,这是业界标准的最佳实践。