Protocol — 是一组要求(属性和方法),实现该协议的类型必须满足这些要求。在 Swift 中,协议是面向协议编程(Protocol-Oriented Programming,POP)的核心元素,无需继承即可实现多态。根据 Swift.org(2025)的数据,Swift 标准库中 85% 的类型都使用了协议。在 Objective-C 中,@protocol 扮演着类似的角色,但有一个限制——仅适用于类。
要点
Protocol 在 Swift 中 — 是一个抽象接口,定义了类型必须满足的要求。协议可以要求属性(带有 getter/setter)、方法(实例和类型)、初始化器和下标。与类不同,协议可以由结构体、枚举和类实现——这提供了纯基于类的语言所不具备的灵活性。
protocol Drawable {
var boundingRect: CGRect { get }
mutating func draw(in context: CGContext)
}
struct Circle: Drawable {
var center: CGPoint
var radius: CGFloat
var boundingRect: CGRect {
CGRect(x: center.x - radius, y: center.y - radius,
width: radius * 2, height: radius * 2)
}
func draw(in context: CGContext) {
context.addEllipse(in: boundingRect)
}
}Circle 结构体实现了 Drawable 协议,提供了 boundingRect 属性和 draw(in:) 方法。借助协议,任何实现 Drawable 的图形都可以统一处理——这是无需从公共基类继承的多态。
Protocol-Oriented Programming(POP) — Apple 在 WWDC 2015 上提出的范式,作为基于类的继承的替代方案。在 POP 中,协议是主要的抽象工具,协议扩展提供默认实现。这解决了「菱形继承」问题,并允许在不修改源代码的情况下扩展现有类型。
| 方面 | POP(面向协议) | OOP(基于类) |
|---|---|---|
| 抽象单元 | Protocol | 基类 |
| 复用 | Protocol extension | 继承 |
| 值类型 | 支持(struct) | 仅引用类型 |
| 多重应用 | Protocol composition | 多重继承(Swift 中不支持) |
| 耦合风险 | 低(松散耦合) | 高(僵化层级) |
根据 Apple Swift 博客(2025)的数据,结构体在现代 Swift 应用中占类型的 70%。POP 是这一转变的关键原因:协议使结构体无需转向类即可获得多态行为。
Swift 协议 位于 Java 接口和 C++ 抽象类之间的中间位置。它们可以包含实现(通过 extension),但没有状态(存储属性)。让我们通过三种语言的例子来分析差异。
| 特性 | Swift Protocol | Java Interface | C++ Abstract Class |
|---|---|---|---|
| 方法实现 | 是(extension) | 是(default methods) | 是 |
| 存储属性 | 否 | 否 | 是 |
| 值类型 | 是 | 否 | 否 |
| 多重实现 | 是 | 是 | 是 |
| 初始化器 | 是(要求) | 否 | 是 |
关键区别:Swift Protocol 可以要求初始化器和下标,这在 Java Interface 中不可用。但协议不能存储状态——这仍然是实现协议的类型所拥有的特权。
Associated Type(关联类型)— 协议内部的通用类型,由实现类型具体指定。这允许创建类型安全的协议,而无需指定具体的数据类型。关联类型与 Swift 泛型协同工作。
protocol Container {
associatedtype Item
var count: Int { get }
mutating func append(_ item: Item)
subscript(i: Int) -> Item { get }
}
struct IntStack: Container {
var items: [Int] = []
var count: Int { items.count }
typealias Item = Int
mutating func append(_ item: Int) {
items.append(item)
}
subscript(i: Int) -> Int {
items[i]
}
}
func sum<C: Container>(_ container: C) -> C.Item where C.Item: Numeric {
// 函数体
}Container — 一个带有 associatedtype Item 的协议。IntStack 实现了它,指定了 typealias Item = Int。泛型函数 sum 使用 where 约束来仅对数值容器进行操作。关联类型使协议通用化而不失类型安全性。
Protocol composition — 通过 & 运算符组合多个协议。满足组合的类型必须实现所有组合的协议。这取代了 Swift 中不存在的多重继承,并允许精确指定函数参数的要求。
protocol Named { var name: String { get } }
protocol Aged { var age: Int { get } }
struct Person: Named, Aged {
let name: String
let age: Int
}
func greet(_ entity: Named & Aged) {
print("你好,\(entity.name),年龄 \(entity.age)!")
}
// Protocol extension — 默认实现
extension Named {
func introduce() {
print("我的名字是\(name)")
}
}Protocol extension 提供方法的默认实现。实现协议的类型可以覆盖 extension 方法——在这种情况下,将调用其自身的实现。这是 Swift 无需 @objc 即可实现可选方法的机制。
@protocol 在 Objective-C 中 — Swift Protocol 的前身,但具有显著的限制。Objective-C 协议仅适用于类(不适用于结构体或枚举),并通过消息传递使用动态派发。方法可以是 @required(默认)或 @optional。
@protocol Loggable
@required
- (void)logMessage: (NSString *)message;
@optional
- (NSString *)logPrefix;
@end
@interface ConsoleLogger : NSObject
@end
@implementation ConsoleLogger
- (void)logMessage: (NSString *)message {
NSLog(@"[LOG] %@", message);
}
@end与 Swift 不同,Objective-C 协议不支持关联类型、泛型、协议扩展和值类型。它们仍然是类的抽象机制,而 Swift Protocol 则是适用于所有类型的全面多态工具。
常见问题
Protocol 不能存储状态(存储属性)——只能对属性提出要求。抽象类可以包含数据字段。协议可以由结构体和枚举实现,而抽象类只能由类实现。在 Swift 中,协议是主要的抽象工具,类使用较少。
POP — 一种范式,其中协议和协议扩展取代了深层的继承层级。POP 不采用从基类继承所有子类的方式,而是使用通过 extension 提供默认实现的协议组合。这降低了耦合度并提高了代码的复用性。
是的,Swift 协议支持继承。protocol SerializableDrawable: Drawable, Codable — 一个结合了 Drawable 和 Codable 要求的协议。实现 SerializableDrawable 的类型必须满足层级中所有协议的要求。这与类继承不同——协议没有共同的祖先。
some(不透明类型)保证函数返回一个符合协议的特定具体类型。any(存在类型)允许存储任何符合协议的类型。some 用于保持类型标识,any 用于异构集合。some 出现在 Swift 5.1 中,any 出现在 Swift 5.7 中。
使用 is 进行检查,使用 as? 进行转换:if let drawable = object as? Drawable { drawable.draw(in: ctx) }。在 Objective-C 中使用 conformsToProtocol:。Swift 也支持对没有关联类型的协议进行 is 检查。
总结
我们将开发一款交钥匙移动应用程序
IT Sectr自2017年以来为初创企业和企业打造iOS和Android应用程序。我们将为您提供咨询并提出最佳解决方案。