any 关键字用于显式地标注协议类型,让代码更清晰并减少对协议类型(existential types)的混淆。
any历史
1、引入any之前
在 Swift 5.6 及更早的版本中,把协议当作类型使用(如 Decoder、Hashable):
Result<Int, Error>
let e: Error
func foo(_ e: Error)
这三种写法表示,一个遵循Error的未知具体类型(存在类型)。
2、引入any之后
从 Swift 5.7 开始引入 any,用于显式区分存在类型或者泛型约束。
这表示协议不再属于默认存在类型,使用协议作为类型需要显式写 any。
s: Shape // 遵循 Shape 协议
s: any Shape // Shape 类型的值
如果使用一个协议类型的值,需要加上 any,如 any Decoder,这是合法且推荐的写法。
不加 any,Swift 会假定泛型约束(或具体类型)。
3、兼容旧代码
目前,Swift 5.7+ 版本中,仍然存在两种写法:
Result<Int, Error> // 兼容旧语法,语义 = any Error
Result<Int, any Error> // 新语法,语义明确
在行为上是等价的,但是语义表达上不等价。
1)协议和类型混用是历史遗留问题,Swift为了兼容旧代码,仍然可以使用协议作为类型使用。
2)使用any标注存在类型,是新语法。官方文档和新API已全面使用 any Error。
但是在某些上下文中,编译器会提示:
Use of protocol ‘Error’ as a type must be written ‘any Error’
这是一种向后兼容+向前引导的策略。
any 用途
1、显式声明存在类型
使用 any,表示这是一个存在类型。
func f(_ e: any Error) // 明确是存在类型
e 可以是任何符合 Error 协议的类型。
2、泛型约束
使用泛型约束(如 T: Protocol)时,编译器会优化为特定的具体类型。
func f<T: Error>(_ e: T) // 泛型约束
3、在函数参数中使用
如果一个参数需要接受协议类型作为值,可以显式地使用 any:
func performAction(with value: any Decodable) {
// 处理符合 Decodable 的值
}
4、在属性或变量中使用
当属性需要存储一个协议类型的值时,any 可以明确类型语义:
var items: [any Hashable] = [1, "Hello", 3.14]
在这个例子中,items 数组可以存储任意符合 Hashable 的值。
any 特点
1、性能区别
使用 any 时,Swift 会将协议类型的值存储在一个 existential container 中,包含类型信息和方法表。这种方式会有一定的性能开销。
使用泛型时,编译器会在编译期优化为具体类型,因此性能更高。
2、语法清晰
使用 any 明确指出一个值是存在类型,而不是泛型。
3、适用场景
需要运行时存储、传递协议类型的值时(例如多态场景),使用 any 是合适的。
如果性能敏感,且类型已知,建议使用泛型。
注意事项
1、协议不能实例化
any可以将协议作为存在类型,但是不能实例化:
protocol Animal {
var sound: String { get }
}
let a: any Animal = Dog()
print(a.sound)
any Animal 是一个类型,可以作为变量类型、参数类型、数组类型,可以在运行期间装不同具体的实现(Dog / Cat)。
但是协议无法实例化:
let a = Animal() // 协议不是构造器
协议没有存储结构、初始化方法和具体实现,所以无法创建实例。
总结
any 的核心作用:让协议类型的语义更加清晰。
当需要处理符合协议的值,而不是具体类型,使用 any。
当性能敏感,或者类型在编译时已知时,使用泛型或具体类型更高效。
