在 Swift 中,protocol 是一种协议,用来定义一组方法、属性或其他要求,而不提供具体的实现。协议可以被类(class)、结构体(struct)和枚举(enum)遵循,从而为它们提供统一的行为规范。
定义和特点
1、定义协议:
使用关键字 protocol 定义协议。
protocol SomeProtocol {
var someProperty: String { get } // 只读属性
func someMethod()
}
定义了一个协议中要求的属性,名为 someProperty,类型是 String。
{ get } 表示这个属性是只读的。
遵循该协议的类型必须实现这个属性,至少能提供一个“获取器”(getter)。
例如,someProperty 可以是一个常量、只读计算属性,或者一个拥有 getter 的存储属性。
2、遵循协议:
使用 : 表示一个类型遵循协议。
struct SomeStruct: SomeProtocol {
var someProperty: String // 实现协议要求的属性
func someMethod() {
print("Method implemented")
}
}
3、多重协议遵循:
一个类型可以遵循多个协议。
protocol AnotherProtocol {
func anotherMethod()
}
class SomeClass: SomeProtocol, AnotherProtocol {
var someProperty: String = "Property"
func someMethod() {
print("Method implemented in class")
}
func anotherMethod() {
print("Another method implemented")
}
}
4、协议可以被扩展:
可以使用 extension 为协议提供默认实现。
protocol Printable {
func printDetails()
}
extension Printable {
func printDetails() {
print("Default implementation of printDetails")
}
}
struct Book: Printable {}
let book = Book()
book.printDetails() // 输出:Default implementation of printDetails
协议的高级特性
1、属性要求:
协议可以要求实现的属性是只读或可读写。
{ get } 表示只读。
{ get set } 表示可读写。
protocol FullyNamed {
var fullName: String { get set }
}
2、方法要求:
协议可以定义实例方法和类方法,但不能提供具体实现。
protocol RandomNumberGenerator {
func random() -> Double
}
3、可选要求:
使用 @objc 修饰的协议可以定义可选方法或属性(仅适用于类类型)。
@objc protocol OptionalProtocol {
@objc optional func optionalMethod()
}
class MyClass: OptionalProtocol {
func optionalMethod() {
print("Implemented optional method")
}
}
在 Swift 中,协议的可选方法或属性是指协议中定义的某些方法或属性可以选择性地由遵循该协议的类型实现,而不是必须实现。这种特性仅适用于用 @objc 修饰的协议(即 Objective-C 兼容协议),并且只能在类类型中使用(不能用于结构体或枚举)。
optional 标记:
在协议中使用 @objc 和 optional 修饰的方法或属性是可选的。
遵循该协议的类可以选择性地实现这些方法或属性,也可以不实现它们。
示例:不实现可选方法
@objc protocol OptionalProtocol {
@objc optional func optionalMethod()
}
class MyClass: OptionalProtocol {
// 不实现 optionalMethod 也不会报错
}
调用可选方法:
因为可选方法或属性可能不存在,调用它们时需要使用可选链(?)。
如果实现了该方法,则调用它;如果没有实现,调用会安全地失败。
let instance: OptionalProtocol = MyClass()
instance.optionalMethod?() // 使用可选链调用
4、协议继承:
一个协议可以继承自其他协议。
protocol InheritingProtocol: SomeProtocol, AnotherProtocol {
func additionalMethod()
}
5、协议作为类型:
协议可以作为类型使用,用于函数参数、返回值或变量。
protocol Shape {
func area() -> Double
}
struct Circle: Shape {
var radius: Double
func area() -> Double {
return .pi * radius * radius
}
}
func printArea(of shape: Shape) {
print("Area is \(shape.area())")
}
let circle = Circle(radius: 5)
printArea(of: circle) // 输出:Area is 78.53981633974483
6、关联类型:
协议可以定义关联类型,用于增加泛型灵活性。
protocol Container {
associatedtype Item
var items: [Item] { get set }
func add(item: Item)
}
struct IntContainer: Container {
typealias Item = Int
var items = [Int]()
func add(item: Int) {
items.append(item)
}
}
7、泛型约束:
协议可以作为泛型约束,用于限制泛型类型。
func display<T: Shape>(_ shape: T) {
print("Shape's area is \(shape.area())")
}
实际应用
1、统一接口:通过协议定义统一的接口,不同类型可以以一致的方式实现。
2、解耦代码:协议使代码更加模块化,便于测试和扩展。
3、面向协议编程(POP):Swift 鼓励使用协议而非继承,强调组合而非继承层级。
总结
protocol 是 Swift 面向协议编程(POP)的核心,提供了灵活的设计方式,用于定义统一的行为规范并支持扩展,是构建模块化和可维护代码的重要工具。
相关文章
1、Swift科普文《associatedtype》: https://fangjunyu.com/2024/10/21/swift%e7%a7%91%e6%99%ae%e6%96%87%e3%80%8aassociatedtype%e3%80%8b/
2、Swift科普文《typealias》: https://fangjunyu.com/2024/10/21/swift%e7%a7%91%e6%99%ae%e6%96%87%e3%80%8atypealias%e3%80%8b/
扩展知识
为什么存在@objc这种需求?
可选方法或属性的主要作用是提供一种更灵活的协议设计,允许部分功能是可选的,而不强制每个遵循者都实现所有内容。
1、处理差异化功能需求
有些协议中的功能可能不是所有遵循者都需要实现的。
通过可选方法,协议可以为遵循者提供默认行为或额外选择,而不增加实现负担。
示例:UITableViewDataSource
在 iOS 开发中,UITableViewDataSource 就是一个典型的例子。它定义了两个必须实现的方法和若干可选方法:
@objc protocol UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
@objc optional func numberOfSections(in tableView: UITableView) -> Int
}
如果开发者的 UITableView 只有一个 section,他们可以不实现 numberOfSections,默认值会被用。
2、与 Objective-C 兼容
Swift 的 @objc 协议和 Objective-C 的协议可以互操作,而 Objective-C 协议本身支持可选方法。
如果需要与旧的 Objective-C 框架或代码交互,使用可选方法可以保证兼容性。
3、提供默认行为
如果协议的扩展(extension)为可选方法提供了默认实现,遵循者即使不实现这些方法,也能使用协议的默认行为。
示例:扩展中的默认实现
@objc protocol OptionalProtocol {
@objc optional func optionalMethod()
}
extension OptionalProtocol {
func optionalMethod() {
print("Default implementation of optionalMethod")
}
}
class MyClass: OptionalProtocol {
// 不实现 optionalMethod,但可以使用扩展的默认实现
}
let instance = MyClass()
instance.optionalMethod?() // 输出:Default implementation of optionalMethod
可选方法总结
是否实现可选方法是类的选择:遵循协议的类可以不实现可选方法或属性,这种灵活性适合差异化需求。
调用时需注意安全性:由于方法可能未实现,调用时要用可选链避免崩溃。
应用场景:
某些功能是额外的,而不是核心需求(如 UITableViewDataSource)。
与 Objective-C 框架交互时。
为协议设计提供灵活性,减少实现负担。
这种设计在 Apple 框架中非常常见,可以减少开发者的负担,同时增强了协议的适配性和灵活性。