在Swift中,static和class关键字都用于定义类型层面的成员(方法和属性),但它们在适用范围、继承性及使用场景上有显著差异。以下是详细说明:
1. 核心区别
| 特性 | static | class |
|---|---|---|
| 适用范围 | 类、结构体、枚举 | 仅类 |
| 是否允许重写 | 不可重写(隐式 final) | 可被子类重写 |
| 存储属性支持 | 支持 | 不支持(仅计算属性和方法) |
2. 使用示例
类的场景
class Parent {
// 可被子类重写的类方法
class func dynamicMethod() {
print("Parent's dynamic method")
}
// 不可重写的类方法(等价于 final class)
static func staticMethod() {
print("Parent's static method")
}
// 类计算属性(可重写)
class var dynamicProperty: Int { 100 }
// 静态存储属性(不可重写)
static let staticProperty: Int = 200
}
class Child: Parent {
// 重写父类的类方法
override class func dynamicMethod() {
print("Child's dynamic method")
}
// 重写父类的类计算属性
override class var dynamicProperty: Int { 200 }
// ❌ 以下代码会报错:static成员不可重写
// override static func staticMethod() { ... }
}
结构体/枚举的场景
struct MyStruct {
// 结构体的类型方法(只能用 static)
static func myMethod() {
print("Struct's static method")
}
}
enum MyEnum {
// 枚举的类型属性(只能用 static)
static var myProperty: Int { 42 }
}
协议中的实现
protocol MyProtocol {
static func protocolMethod()
}
class MyClass: MyProtocol {
// 使用 class 实现协议方法(允许子类重写)
class func protocolMethod() {
print("Class's protocol implementation")
}
}
struct MyStruct: MyProtocol {
// 使用 static 实现协议方法
static func protocolMethod() {
print("Struct's protocol implementation")
}
}
3. 场景对比
| 场景 | 推荐关键字 | 原因 |
|---|---|---|
| 需要子类重写的方法 | class | 支持动态派发和多态24 |
| 结构体/枚举的类型成员 | static | 唯一可用选项16 |
| 类的不可变类型常量 | static | 内存高效,全局唯一15 |
| 类的存储类型属性 | static | class无法修饰存储属性35 |
| 需禁止子类修改的类方法 | static | 编译时绑定,不可重写 |
4. 原理说明
-
static关键字:
适用于所有类型(类、结构体、枚举),定义的成员隐式标记为final,禁止重写。对于类,static成员等价于final class,确保类型成员的行为在编译期确定。 -
class关键字:
仅用于类,允许子类通过override重写。由于Swift的类继承特性,class成员在运行时动态派发(除非被标记为final或由编译器优化)。 -
存储属性的限制:
class不能修饰存储属性,因为类的类型存储属性需要在全局唯一地址存储,而static通过编译期确定的全局存储实现这一目标。 -
内存分配:
static属性在类型加载时分配内存,全局唯一;class方法通过虚函数表动态派发。 -
语法等价性:
static var在类中等价于final class var,均禁止重写。
5. 总结
- 使用
class需要支持子类重写(仅类可用)。 - 使用
static需要跨类型通用(类、结构体、枚举)或禁止重写。 - 协议中声明类型成员时,统一用
static,由具体类型决定是否可重写(类用class,结构体/枚举用static)。