Swift 是一种强类型的编程语言,支持面向对象和函数式编程范式。协议(Protocol)是 Swift 中的一个重要特性之一,也是面向对象编程中常用的机制之一,它定义了方法、属性和其他成员的规范,而不关心具体实现。本文将全面介绍 Swift 中的协议,包括协议的基础知识、扩展协议、协议组合、协议继承、自定义类型实现协议等。
基础知识
定义协议
在 Swift 中,可以使用 protocol
关键字来定义一个协议,如下所示:
1
2
3
protocol MyProtocol {
// 协议内容
}
协议中可以定义属性、方法和其他成员。与类和结构体不同,协议只定义规范,不提供具体的实现。在定义协议时,可以指定协议是否需要实现某些成员,如果某个成员是必须实现的,可以使用 required
关键字。
实现协议
在 Swift 中,任何类型都可以实现一个或多个协议。要实现一个协议,需要使用 class
、struct
或者 enum
关键字,并在名称后面遵循协议列表,如下所示:
1
2
3
class MyClass: MyProtocol {
// 类的实现
}
在实现协议时,必须提供协议中定义的所有成员,包括必须实现的成员。如果一个类型不能完全实现一个协议,可以将该类型声明为 protocol
的扩展或子协议,以允许其他类型实现缺失的部分。
协议中的属性
在协议中,可以定义计算型属性和存储型属性。计算型属性只定义 getter 和 setter,不包含实际的存储值;存储型属性包含实际的存储值。协议中的属性定义和普通属性定义非常相似,只是省略了花括号,如下所示:
1
2
3
protocol MyProtocol {
var myProperty: Int { get set }
}
在实现协议时,必须提供协议中定义的所有属性,包括计算型属性和存储型属性。如果协议中定义的属性是可读的,但不需要写操作,可以省略 get
关键字。
协议中的方法
在协议中,可以定义实例方法和类方法,方法的参数和返回值也需要被定义。协议中的方法与普通方法定义类似,只是省略了花括号,如下所示:
1
2
3
4
protocol MyProtocol {
func myMethod(arg1: Int, arg2: String) -> Bool
static func myClassMethod() -> Void
}
与实现协议中的属性类似,在实现协议中的方法时,必须提供协议中定义的所有方法,包括实例方法和类方法。
协议中的可选成员
在 Swift 中,协议中的成员可以是可选的。如果一个成员是可选的,实现这个协议时可以选择不实现这个成员。要定义可选成员,可以使用 optional
关键字,如下所示:
1
2
3
@objc protocol MyProtocol {
@objc optional func myOptionalMethod()
}
在定义可选成员时,需要将协议标记为 @objc
,并使用 optional
关键字定义一个可选成员方法或属性。在实现协议时,可以通过可选绑定(Optional Binding)来检查该成员是否被实现。
协议中的 mutating 方法
在 Swift 中,结构体和枚举是值类型,而不是引用类型。在函数中修改结构体或枚举的属性需要使用 mutating
关键字来标记该方法是可变的。同样地,在协议中定义的方法中修改实现类型的属性也需要使用 mutating
关键字,如下所示:
1
2
3
protocol MyProtocol {
mutating func myMutatingMethod()
}
在实现协议时,如果实现类型是一个类,则不需要使用 mutating
关键字。
扩展协议
在 Swift 中,可以对协议进行扩展(Extension),添加额外的方法、属性和嵌套类型。扩展协议可以为遵循该协议的所有类型添加新功能,而无需修改这些类型的源代码。要扩展一个协议,可以使用 extension
关键字,如下所示:
1
2
3
4
5
extension MyProtocol {
func myNewMethod() -> Void {
// 新方法的实现
}
}
在协议中定义了一个方法后,可以在扩展协议的时候添加默认的实现。当遵循该协议的类型没有提供自己的实现时,会使用默认的实现。
协议组合
Swift 支持将多个协议组合成一个更大的协议,以精确地描述类型的规范。协议组合可以使用 &
运算符连接两个或多个协议,如下所示:
1
2
3
4
5
6
7
8
9
10
11
protocol Protocol1 {
// 协议内容
}
protocol Protocol2 {
// 协议内容
}
class MyClass: Protocol1 & Protocol2 {
// 类的实现
}
在上面的例子中,MyClass
类同时遵循了 Protocol1
和 Protocol2
两个协议。
协议继承
在 Swift 中,一个协议可以继承另一个协议,从而获得父协议定义的所有方法、属性和其他成员。协议继承可以使用 :
运算符连接父协议和子协议,如下所示:
1
2
3
4
5
6
7
protocol ParentProtocol {
// 父协议内容
}
protocol ChildProtocol: ParentProtocol {
// 子协议内容
}
在上面的例子中,ChildProtocol
继承了 ParentProtocol
的所有成员。
类型转换
Swift中,我们可以使用is
和as
运算符来检查实例是否遵循某个协议并将类型转换为遵循该协议的类型。
1
2
3
4
5
6
7
8
9
10
11
12
protocol SomeProtocol {
// 协议内容
}
class SomeClass: SomeProtocol {
// 类型实现协议中定义的内容
}
let someObject: AnyObject = SomeClass() // 定义一个AnyObject类型的变量,并将其赋值为SomeClass类型的实例
if let someProtocolObject = someObject as? SomeProtocol { // 将someObject转换为SomeProtocol协议类型的变量
// 如果someObject遵循SomeProtocol协议,则进入条件语句块
}
在上面的例子中,定义了一个AnyObject
类型的变量someObject
,并将其赋值为SomeClass
类型的实例。在使用as?
运算符将someObject
转换为SomeProtocol
协议类型的变量时,如果someObject
确实遵循SomeProtocol
协议,则转换成功,并将结果绑定到新变量someProtocolObject
中。如果someObject
不遵循SomeProtocol
协议,则转换失败,someProtocolObject
的值为nil
。
委托模式
委托模式是Swift中协议的一种常见用法。它允许一个对象通过协议来委托或者代表另一个对象完成某些任务或者获取某些信息。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
protocol SomeDelegate {
func didDoSomething()
}
class SomeClass {
var delegate: SomeDelegate?
func doSomething() {
// 完成某些任务
delegate?.didDoSomething() // 在任务完成后,通知delegate对象
}
}
class AnotherClass: SomeDelegate {
// 实现SomeDelegate协议中的方法
func didDoSomething() {
print("AnotherClass did something")
}
}
let someObj = SomeClass()
let anotherObj = AnotherClass()
someObj.delegate = anotherObj // 在SomeClass对象中设置AnotherClass对象为delegate
someObj.doSomething() // 调用SomeClass对象中的doSomething方法,完成任务并通知delegate对象
在上面的例子中,SomeClass
定义了一个名为delegate
的属性,用于存储遵循SomeDelegate
协议的对象,并在完成某些任务后通知它。AnotherClass
遵循SomeDelegate
协议,并且实现了协议中定义的方法,因此可以作为SomeClass
的delegate
对象。通过设置delegate
属性,实现了将任务委托给AnotherClass
对象,并在任务完成后通知它。
自定义类型实现协议
除了系统提供的类型之外,我们还可以创建自定义类型并让它们遵循任何协议。下面是一个简单的例子:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
protocol Printable {
var description: String { get }
}
struct MyStruct: Printable {
var description: String {
return "I'm a struct."
}
}
class MyClass: Printable {
var description: String {
return "I'm a class."
}
}
let myStruct = MyStruct()
let myClass = MyClass()
print(myStruct.description) // 输出:I'm a struct.
print(myClass.description) // 输出:I'm a class.
在上面的例子中,MyStruct
和 MyClass
分别实现了 Printable
协议,并重写了 description
属性。在最后的输出中,我们可以看到它们分别输出了不同的结果。