第2章:Swift 协议的基础
2.2 属性要求(Property Requirements)
在 Swift 的协议中,属性要求是定义类型必须具备的属性的一种方式。通过属性要求,协议可以指定遵循者需要提供哪些数据接口,以及这些属性的读写权限。属性要求是协议化编程(POP)的核心组成部分之一,它为类型提供了一致的访问方式,同时保持了实现的灵活性。本节将详细探讨属性要求的语法、使用场景和注意事项,并通过示例展示其实际应用。
属性要求的语法
协议中的属性要求使用熟悉的属性声明语法,但不包含具体实现,只需指定名称、类型和访问权限(get 或 get set)。基本形式如下:
protocol ProtocolName {
var propertyName: Type { get } // 只读属性
var anotherProperty: Type { get set } // 可读写属性
}
{ get }:表示属性必须可读,遵循者可以选择提供存储属性或计算属性的只读实现。{ get set }:表示属性必须可读写,遵循者需要提供可读写的实现,通常通过变量(var)实现。
例如:
protocol Identifiable {
var id: String { get }
var isActive: Bool { get set }
}
在这里,id 是只读属性,isActive 是可读写属性。
实现属性要求
遵循协议的类型必须满足属性要求,可以通过存储属性或计算属性实现。例如:
struct User: Identifiable {
let id: String // 只读存储属性
var isActive: Bool // 可读写存储属性
}
class Account: Identifiable {
var id: String { // 只读计算属性
return UUID().uuidString
}
var isActive: Bool // 可读写存储属性
}
User使用存储属性(let和var)实现要求,id是不可变的,isActive是可变的。Account使用计算属性实现id,每次访问时生成一个新的 UUID,同时用存储属性实现isActive。
这种灵活性让遵循者可以根据需求选择最适合的实现方式。
只读与可读写的区别
属性要求的读写权限直接影响遵循者的实现:
- 只读属性(
{ get }):遵循者可以提供存储属性(let或var)、只读计算属性,甚至是带有private set的属性,只要外部可读即可。 - 可读写属性(
{ get set }):遵循者必须提供外部可读写的属性,通常是var存储属性或带有get和set的计算属性。
例如:
protocol Adjustable {
var level: Int { get set }
}
struct Volume: Adjustable {
var level: Int // 直接使用存储属性
init(level: Int) {
self.level = level
}
}
struct Brightness: Adjustable {
private var _level: Int // 私有存储
var level: Int { // 计算属性实现可读写
get { _level }
set { _level = max(0, min(100, newValue)) }
}
init(level: Int) {
self._level = level
}
}
Volume 使用简单的存储属性,而 Brightness 通过计算属性限制 level 的范围(0 到 100),两种方式都满足了 get set 要求。
静态属性要求
协议不仅可以定义实例属性,还可以定义静态属性(类型属性),使用 static 关键字。例如:
protocol Categorizable {
static var category: String { get }
}
struct Book: Categorizable {
static var category: String = "Literature"
}
print(Book.category) // 输出: Literature
静态属性要求常用于定义与类型本身相关的属性,而不是实例特定的数据。
属性要求的实际应用
属性要求在建模和抽象中非常有用。例如,假设我们要设计一个表示“形状”的协议:
protocol Shape {
var area: Double { get } // 只读,表示面积
var isVisible: Bool { get set } // 可读写,表示可见性
}
struct Circle: Shape {
let radius: Double
var isVisible: Bool
var area: Double {
return Double.pi * radius * radius
}
}
struct Rectangle: Shape {
let width: Double
let height: Double
var isVisible: Bool
var area: Double {
return width * height
}
}
var circle = Circle(radius: 5, isVisible: true)
print(circle.area) // 输出: 78.53981633974483
circle.isVisible = false // 修改可见性
在这个例子中,Shape 协议通过属性要求统一了不同形状的接口,Circle 和 Rectangle 根据自身特点实现了 area 和 isVisible。
注意事项
- 实现必须匹配权限:如果协议要求
{ get set },遵循者不能只提供{ get },否则会编译错误。 - 存储 vs 计算:协议不关心属性是存储属性还是计算属性,只要满足访问权限即可。
- 默认值不可用:协议中不能为属性指定默认值,默认值只能在遵循者中实现。
- 类型推断:属性类型必须明确指定,不能依赖类型推断。
小结
属性要求是协议的核心功能之一,它通过定义只读或可读写的属性,为遵循者提供了数据访问的规范。无论是简单的存储属性还是复杂的计算属性,Swift 的灵活性让属性要求的实现方式多种多样。在下一节,我们将探讨协议中的方法要求,进一步扩展协议的功能性。
