Swift 协议:protocol 定义与实现
在 Swift 中,协议(protocol)是一种定义方法、属性或其他功能要求的蓝图。任何遵循该协议的类型(如结构体、类或枚举)都必须提供这些要求的具体实现。使用协议可以让你编写更灵活、可复用和解耦的代码。
定义一个基本协议
- 打开 Xcode 或任意文本编辑器,创建一个新的 Swift 文件(例如
MyProtocol.swift)。 - 输入以下代码来定义一个名为
Describable的协议:
protocol Describable {
var description: String { get }
}
这段代码表示:任何遵循 Describable 协议的类型,都必须提供一个名为 description 的只读字符串属性。
- 注意:协议中声明的属性不需要指定存储还是计算,只需要说明它是只读(
get)还是可读写(get set)。
让结构体遵循协议
- 定义一个结构体并让它遵循
Describable协议:
struct Person: Describable {
let name: String
let age: Int
var description: String {
return "姓名:\(name),年龄:\(age)"
}
}
-
验证协议是否被正确实现:
- 结构体
Person在类型名后加上冒号和协议名: Describable,表示它遵循该协议。 - 它提供了
description属性的实现,符合协议要求。
- 结构体
-
测试功能是否正常:
let p = Person(name: "张三", age: 25)
print(p.description) // 输出:姓名:张三,年龄:25
让类遵循协议
- 创建一个类并实现同一协议:
class Car: Describable {
let brand: String
let model: String
init(brand: String, model: String) {
self.brand = brand
self.model = model
}
var description: String {
return "\(brand) \(model)"
}
}
- 使用该类:
let myCar = Car(brand: "Tesla", model: "Model 3")
print(myCar.description) // 输出:Tesla Model 3
协议中的方法要求
- 扩展协议,加入方法要求:
protocol Resizable {
func resize(to size: Double) -> Bool
mutating func resetSize()
}
resize(to:)是一个返回Bool的实例方法。resetSize()前有mutating关键字,表示该方法可能修改值类型的自身状态(对类无效,但对结构体和枚举必需)。
- 让结构体实现该协议:
struct Rectangle: Resizable {
var width: Double
var height: Double
func resize(to size: Double) -> Bool {
if size > 0 {
width = size
height = size
return true
}
return false
}
mutating func resetSize() {
width = 1.0
height = 1.0
}
}
- 调用协议方法:
var rect = Rectangle(width: 2.0, height: 3.0)
rect.resize(to: 5.0) // 成功调整
rect.resetSize() // 重置为默认大小
协议作为类型使用
- 将协议用作变量、常量或参数的类型:
func printDescription(of item: Describable) {
print(item.description)
}
let person = Person(name: "李四", age: 30)
let car = Car(brand: "Toyota", model: "Camry")
printDescription(of: person) // 正常调用
printDescription(of: car) // 同样正常调用
- 创建协议类型的数组:
let items: [Describable] = [person, car]
for item in items {
print(item.description)
}
协议继承与组合
- 定义一个新协议继承已有协议:
protocol AdvancedDescribable: Describable {
var detailedInfo: String { get }
}
任何遵循 AdvancedDescribable 的类型,必须同时满足 Describable 和 detailedInfo 的要求。
- 实现继承后的协议:
struct Product: AdvancedDescribable {
let name: String
let price: Double
var description: String {
return "商品:\(name)"
}
var detailedInfo: String {
return "名称:\(name),价格:¥\(price)"
}
}
- 组合多个协议(使用
&):
func handleItem(_ item: Describable & Resizable) {
print(item.description)
_ = item.resize(to: 10.0)
}
只有同时遵循 Describable 和 Resizable 的类型才能传入此函数。
可选协议要求(仅限 Objective-C 兼容)
- 在协议前加上
@objc并标记方法为optional(仅适用于类,且需继承自NSObject):
@objc protocol OptionalProtocol {
@objc optional func doSomething()
@objc optional var optionalValue: Int { get }
}
- 实现时可选择性提供:
class MyClass: NSObject, OptionalProtocol {
// 可以不实现 doSomething 或 optionalValue
}
- 调用时需用可选链:
let obj = MyClass()
obj.doSomething?() // 安全调用
注意:除非需要与 Objective-C 交互,否则应避免使用可选协议,因为会牺牲类型安全。
协议扩展:提供默认实现
- 为协议添加默认行为:
extension Describable {
func log() {
print("[LOG] \(description)")
}
}
- 所有遵循
Describable的类型自动获得log()方法:
person.log() // 输出:[LOG] 姓名:李四,年龄:30
car.log() // 输出:[LOG] Toyota Camry
- 类型仍可自定义覆盖默认实现:
struct CustomThing: Describable {
var description: String { "自定义" }
func log() {
print(">>> \(description)")
}
}
检查类型是否遵循协议
- 使用
is检查:
if car is Describable {
print("car 遵循 Describable")
}
- 使用
as?安全转换:
let unknown: Any = car
if let describable = unknown as? Describable {
print(describable.description)
}
- 使用
as!强制转换(仅在确定时使用):
let d = unknown as! Describable
协议与泛型结合
- 定义泛型函数,约束类型必须遵循某协议:
func duplicate<T: Describable>(_ item: T) -> [T] {
return [item, item]
}
- 调用时传入符合协议的类型:
let people = duplicate(person) // 返回 [Person, Person]
常见错误与避坑指南
| 错误现象 | 原因 | 解决方法 |
|---|---|---|
| 编译报错 “Type does not conform to protocol” | 未实现协议要求的属性或方法 | 检查协议定义,补全缺失的实现 |
结构体中修改属性的方法未加 mutating |
值类型方法默认不能修改自身 | 在方法前添加 mutating 关键字 |
| 协议方法调用崩溃 | 使用了 @objc optional 但未检查是否存在 |
使用 ? 可选调用,如 obj.method?() |
| 无法将协议用于泛型约束 | 协议包含关联类型(如 Self 或 associatedtype) |
改用泛型约束或具体类型 |
最佳实践
- 优先使用协议而非基类:Swift 推崇“面向协议编程”,比类继承更灵活。
- 协议命名用形容词:如
Equatable、Comparable、CustomStringConvertible,而不是名词。 - 小而专注:一个协议只做一件事,便于组合复用。
- 通过扩展提供默认实现:减少重复代码,同时保留自定义能力。
- 避免过度设计:不要为了“看起来高级”而滥用协议,确保有实际收益。

暂无评论,快来抢沙发吧!