Swift 在设计上,主要设计了三种多态类型,类型设计的基础是 Protocol,先定义一个 Protocol:
protocol Shape {
func draw() -> String
}1、泛型(Generic)
swift 的泛型和 rust 等语言一致,属于编译期泛型,在编译期间替换为具体类型,无运行时开销。相当于模版;目的:开发者可以设计实现用更少的代码实现更多的功能。<T: SomeType>表示是一个泛型限定。
protocol Shape {
func draw() -> String
}
struct Triangle: Shape {
var size: Int
func draw() -> String {
(1...size).map { String(repeating: "*", count: $0) }.joined(separator: "\n")
}
}
struct FlippedShape<T: Shape>: Shape {
var shape: T
func draw() -> String {
shape.draw().split(separator: "\n").reversed().joined(separator: "\n")
}
}
// 工厂:返回具体泛型类型 FlippedShape<T>
func makeFlippedGeneric<T: Shape>(_ s: T) -> FlippedShape<T> {
FlippedShape(shape: s)
}
let tri = Triangle(size: 3)
let flippedGeneric = makeFlippedGeneric(tri)
// flippedGeneric 的类型是 FlippedShape<Triangle> —— caller 知道确切类型
print(type(of: flippedGeneric)) // FlippedShape<Triangle>
print(flippedGeneric.draw())1.1 associatedtype 协议里的关联类型-编译期确定具体类型
protocol Shape {associatedtypeResultfuncdraw() ->Result}可以在 Protocol 中指定一个泛型 associatedtype 类型,实现方实现的时候指定确定的类型,实现方有两种方式实现:
1.1.1 使用 typealias 指定具体类型
struct Triangle: Shape {
typealias Result = String
var size: Int
func draw() -> Result {
(1...size).map { String(repeating: "*", count: $0) }.joined(separator: "\n")
}
}1.1.2 让编译器自己推断
编译器也可以自己推断类型,无需 typealias 指定:
struct Triangle: Shape {
var size: Int
func draw() -> String {
(1...size).map { String(repeating: "*", count: $0) }.joined(separator: "\n")
}
}1.1.3 使用 where 子句限制 typealias 的具体类型
如果使用了 typealias,再定义一个功能函数的时候,可能也需要额外处理:

上边代码 FlippedShape 有个属性是 shape 类型,内部需要对这个属性进行操作,但是 shape.draw() 返回的是 associatedtype 关联类型。所以无法调用任何方法,此时可以使用 where 子句限制关联类型:
struct FlippedShape<T: Shape>: Shape where T.Result == String {
var shape: T
func draw() -> String {
let r = shape.draw().split(separator: "\n").reversed().joined(separator: "\n")
return r;
}
}注意:where 子句在这种场景只能用于泛型。
1.1.4 包装一层实现类型擦除: 使用 where 在内部限定
struct AnyShape: Shape {
typealias Result = String
let _draw: () -> Result
init<T: Shape>(_ input: T) where T.Result == String {
_draw = input.draw;
}
func draw() -> Result {
return _draw();
}
}
func typeRemove() {
let t = Triangle(size: 3)
let anyShape: AnyShape = AnyShape(t);
}注意,这里将泛型定义在 init 函数上,而不是 AnyShape struct 本身。
2、协议类型(Existential 协议存在类型,Boxed 类型)
运行时类型,动态派发,更灵活,但有运行时开销:
protocol Shape {
func draw() -> String
}
func makeShape(_ type: Int) -> any Shape {
if type == 0 {
return Triangle()
} else {
return Square()
}
}makeShape返回一个协议类型 Shape,可能是 Triangle 也可能是 Square,需要运行时才能确定。所以不能把一个协议类型赋给一个泛型:
func makeShape(_ type: Int) -> Shape {
if type == 0 {
return Triangle()
} else {
return Square()
}
}
// 工厂:返回具体泛型类型 FlippedShape<T>
func makeFlippedGeneric<T: Shape>(_ s: T) -> FlippedShape<T> {
FlippedShape(shape: s)
}
let protocolType = makeShape();
//
let tttt = makeFlippedGeneric(makeShape()); // 编译报错 ❌,无法得知 makeShape 的具体类型makeFlippedGeneric 是个泛型函数,需要在编译时就确定参数的具体类型,而 makeShape 是个协议类型,直到运行时才知道具体类型,所以无法赋值。
2.1 Swift 5.6 引入 any Shape 显式表面是类型存在类型
Swift 团队发现,这种隐式的存在类型写法,和泛型很难区分,会在泛型与协议混用时造成大量困惑,比如:
func process<T: Shape>(_ value: T) { ... } // 泛型约束
func process(_ value: Shape) { ... } // 存在类型(动态)看起来只差一点,但性能、行为完全不同!于是 Swift 设计组决定:
✅ 从 Swift 5.6 开始,存在类型必须显式写成 any Shape,否则容易误导。
func draw(_ shape: Shape) { ... } // ❌ 警告(未来版本不推荐)
func draw(_ shape: any Shape) { ... } // ✅ 明确表示是存在类型在 Swift 5.6–5.9 之间:
- 不写 any 仍然能编译,但会出现警告提示:“Implicitly using the existential ‘Shape’ as a generic constraint is deprecated; use ‘any Shape’ instead.”
在 Swift 6(即将正式发布) 中:
- 必须写 any,否则会报错。
3、不透明类型 (Opaque Type)
编译时类型,自己知道是什么类型,对调用者隐藏细节。
public func makeComplexShape() -> some Shape {
let smallTriangle = Triangle(size: 3)
let flipped = FlippedShape(shape: smallTriangle)
return JoinedShape(top: smallTriangle, bottom: flipped)
}这表示:
我(函数实现者)知道返回的具体类型是什么(JoinedShape<Triangle, FlippedShape<Triangle>>),
所以:
- 对调用者来说,它只是“某种符合 Shape 的类型”;
- 对编译器来说,它依然是静态已知的具体类型;
- 对性能来说,没有动态分发的开销。
总结
Unsupported Notion block: table