Swift Protocols
Protocols define the methods and properties required to implement a particular functionality.
Any type that satisfies the requirements of a protocol is said to conform to the protocol.
Classes, structs, or enum types can all conform to protocols and provide concrete implementations to fulfill the methods and functionality defined by the protocol.
Swift protocols are blueprints that define methods, properties, and other requirements suited to a particular task or feature. Protocols can be adopted by classes, structs, and enums to provide concrete implementations of these requirements.
Protocols are a very powerful feature in the Swift language that allow developers to define interfaces, specifying the properties and methods a type must have, without specifying the concrete implementations of those properties and methods.
Syntax
The syntax format of a protocol is as follows:
protocol SomeProtocol {
// 这里可以定义协议要求
var someProperty: Int { get set }
func someMethod()
}
SomeProtocol defines a readable and writable property someProperty and a method someMethod.
To make a class conform to a protocol, add the protocol name after the type name, separated by a colon:as part of the type definition. When conforming to multiple protocols, separate each protocol with a comma,separated.
struct SomeStructure: FirstProtocol, AnotherProtocol {
// 结构体内容
}
If a class has a superclass while conforming to a protocol, place the superclass name before the protocol name, separated by a comma.
class SomeClass: SomeSuperClass, FirstProtocol, AnotherProtocol {
// 类的内容
}
Property Requirements
Protocols are used to specify particular instance properties or type properties, without specifying whether they are stored or computed properties. In addition, you must indicate whether they are read-only or readable and writable.
In protocols, var is usually used to declare variable properties. After the type declaration, add{ set get }to indicate the property is readable and writable. For read-only properties, use{ get }to indicate.
Examples
var marks: Int { get set }
var result: Bool { get }
func attendance() -> String
func markssecured() -> String
}
protocol ClassB: ClassA {
var present: Bool { get set }
var subject: String { get set }
var stname: String { get set }
}
class ClassC: ClassB {
var marks = 96
let result = true
var present = false
var subject = "Swift Protocols"
var stname = "Protocols"
func attendance() -> String {
return "The \(stname) has secured 99% attendance"
}
func markssecured() -> String {
return "\(stname) has scored \(marks) marks"
}
}
let studdet = ClassC()
studdet.stname = "Swift"
studdet.marks = 98
print(studdet.markssecured())
print(studdet.attendance())
print("Marks: \(studdet.marks)")
print("Result: \(studdet.result)")
print("Present: \(studdet.present)")
print("Subject: \(studdet.subject)")
print("Student Name: \(studdet.stname)")
The output of the above program execution is:
Swift has scored 98 marks The Swift has secured 99% attendance Marks: 98 Result: true Present: false Subject: Swift 协议 Student Name: Swift
Mutating Method Requirements
Sometimes you need to modify an instance within its method.
For example, in instance methods of value types (structs, enums), prefix the function with the mutating keyword before func, indicating that the method is allowed to modify the instance it belongs to and the values of its instance properties.
protocol Daysofaweek {
mutating func show()
}
enum Days: Daysofaweek {
case sun, mon, tue, wed, thurs, fri, sat
mutating func show() {
switch self {
case .sun:
self = .sun
print("Sunday")
case .mon:
self = .mon
print("Monday")
case .tue:
self = .tue
print("Tuesday")
case .wed:
self = .wed
print("Wednesday")
case .thurs:
self = .thurs
print("Thursday")
case .fri:
self = .fri
print("Friday")
case .sat:
self = .sat
print("Saturday")
}
}
}
var res = Days.wed
res.show()
The output of the above program execution is:
Wednesday
Initializer Requirements
Protocols can require conforming types to implement specific initializers.
You can write initializer declarations in a protocol definition just like writing ordinary initializers, but without curly braces and the initializer body. The syntax is as follows:
protocol SomeProtocol {
init(someParameter: Int)
}
Examples
protocol tcpprotocol {
init(aprot: Int)
}
Implementing Protocol Initializer Requirements in Classes
You can implement the initializer in a class that conforms to the protocol, and designate it as either a designated initializer or a convenience initializer for the class. In both cases, you must mark the initializer implementation with the "required" modifier:
class SomeClass: SomeProtocol {
required init(someParameter: Int) {
// 构造器实现
}
}
protocol tcpprotocol {
init(aprot: Int)
}
class tcpClass: tcpprotocol {
required init(aprot: Int) {
}
}
Using the required modifier ensures that all subclasses that conform to the protocol also provide an explicit implementation or inherit an implementation for the initializer requirement.
If a subclass overrides a designated initializer from its superclass, and that initializer satisfies a protocol requirement, the initializer implementation must be marked with both required and override modifiers:
protocol TcpProtocol {
init(no1: Int)
}
class MainClass {
var no1: Int // 局部变量
init(no1: Int) {
self.no1 = no1 // 初始化
}
}
class SubClass: MainClass, TcpProtocol {
var no2: Int
init(no1: Int, no2: Int) {
self.no2 = no2
super.init(no1: no1)
}
// 因为遵循协议,需要加上"required"; 因为继承自父类,需要加上"override"
required override convenience init(no1: Int) {
self.init(no1: no1, no2: 0)
}
}
let res = MainClass(no1: 20)
let show = SubClass(no1: 30, no2: 50)
print("res is: \(res.no1)")
print("show.no1 is: \(show.no1)")
print("show.no2 is: \(show.no2)")
The output of the above program execution is:
res is: 20 show.no1 is: 30 show.no2 is: 50
Protocols as Types
Even though protocols do not implement any functionality themselves, they can be used as types.
Protocols can be used like any other ordinary type. Use cases:
- As parameter types or return types in functions, methods, or initializers
- As the type of constants, variables, or properties
- As element types in arrays, dictionaries, or other containers
Examples
protocol Generator {
associatedtype Members
func next() -> Members?
}
// 示例代码使用 Swift 标准库中的迭代器和 `map` 函数
// 使用数组的迭代器
var items = [10, 20, 30].makeIterator()
while let x = items.next() {
print(x)
}
// 使用 `map` 函数
for list in [1, 2, 3].map({ i in i * 5 }) {
print(list)
}
// 直接打印数组
print([100, 200, 300])
print([1, 2, 3].map({ i in i * 10 }))
The output of the above program execution is:
10 20 30 5 10 15 [100, 200, 300] [10, 20, 30]
Adding Protocol Members with Extensions
We can use extensions to extend existing types (classes, structs, enums, etc.).
Extensions can add members such as properties, methods, subscripts, protocols, etc. to existing types.
protocol AgeClassificationProtocol {
var age: Int { get }
func agetype() -> String
}
class Person {
let firstname: String
let lastname: String
var age: Int
init(firstname: String, lastname: String, age: Int = 10) {
self.firstname = firstname
self.lastname = lastname
self.age = age
}
}
extension Person: AgeClassificationProtocol {
func fullname() -> String {
return "\(firstname) \(lastname)"
}
func agetype() -> String {
switch age {
case 0...2:
return "Baby"
case 3...12: // 修正为从3开始
return "Child"
case 13...19:
return "Teenager"
case let x where x > 65:
return "Elderly"
default:
return "Normal"
}
}
}
// 测试代码
let person1 = Person(firstname: "John", lastname: "Doe", age: 25)
let person2 = Person(firstname: "Jane", lastname: "Smith", age: 70)
let person3 = Person(firstname: "Baby", lastname: "Yoda", age: 1)
print("\(person1.fullname()) is a \(person1.agetype())")
print("\(person2.fullname()) is a \(person2.agetype())")
print("\(person3.fullname()) is a \(person3.agetype())")
The output of the above code is:
John Doe is a Normal Jane Smith is a Elderly Baby Yoda is a Baby
Protocol Inheritance
A protocol can inherit one or more other protocols, and can add further requirements on top of the inherited protocols.
The syntax for protocol inheritance is similar to class inheritance. Multiple inherited protocols are separated by commas:
protocol InheritingProtocol: SomeProtocol, AnotherProtocol {
// 协议定义
}
Examples
protocol Classa {
var no1: Int { get set }
func calc(sum: Int)
}
protocol Result {
func print(target: Classa)
}
class Student2: Result {
func print(target: Classa) {
target.calc(sum: 1)
}
}
class Classb: Result {
func print(target: Classa) {
target.calc(sum: 5)
}
}
class Student: Classa {
var no1: Int = 10
func calc(sum: Int) {
no1 -= sum
print("学生尝试 \(sum) 次通过")
if no1 <= 0 {
print("学生缺席考试")
}
}
}
class Player {
var stmark: Result!
init(stmark: Result) {
self.stmark = stmark
}
func print(target: Classa) {
stmark.print(target: target)
}
}
var marks = Player(stmark: Student2())
var marksec = Student()
marks.print(target: marksec)
marks.print(target: marksec)
marks.print(target: marksec)
marks.stmark = Classb()
marks.print(target: marksec)
marks.print(target: marksec)
marks.print(target: marksec)
The output of the above program execution is:
学生尝试 1 次通过 学生尝试 1 次通过 学生尝试 1 次通过 学生尝试 5 次通过 学生尝试 5 次通过 学生缺席考试 学生尝试 5 次通过 学生缺席考试
Class-Only Protocols
You can restrict a protocol to class types only by adding the class keyword to its inheritance list.
The class keyword must be the first item in the protocol's inheritance list, followed by the other inherited protocols. The format is as follows:
protocol SomeClassOnlyProtocol: class, SomeInheritedProtocol {
// 协议定义
}
Examples
protocol TcpProtocol {
init(no1: Int)
}
class MainClass {
var no1: Int // 局部变量
init(no1: Int) {
self.no1 = no1 // 初始化
}
}
class SubClass: MainClass, TcpProtocol {
var no2: Int
init(no1: Int, no2: Int) {
self.no2 = no2
super.init(no1: no1)
}
// 因为遵循协议,需要加上"required"; 因为继承自父类,需要加上"override"
required override convenience init(no1: Int) {
self.init(no1: no1, no2: 0)
}
}
let res = MainClass(no1: 20)
let show = SubClass(no1: 30, no2: 50)
print("res is: \(res.no1)")
print("show.no1 is: \(show.no1)")
print("show.no2 is: \(show.no2)")
The output of the above program execution is:
res is: 20 show.no1 is: 30 show.no2 is: 50
Protocol Composition
Swift supports composing multiple protocols, which is very useful when we need to conform to multiple protocols at the same time.
The syntax format is as follows:
protocol Stname {
var name: String { get }
}
protocol Stage {
var age: Int { get }
}
struct Person: Stname, Stage {
var name: String
var age: Int
}
func show(celebrator: Stname & Stage) {
print("\(celebrator.name) is \(celebrator.age) years old")
}
let studname = Person(name: "Priya", age: 21)
show(celebrator: studname)
let stud = Person(name: "Rehan", age: 29)
show(celebrator: stud)
let student = Person(name: "Roshan", age: 19)
show(celebrator: student)
The output of the above program execution is:
Priya is 21 years old Rehan is 29 years old Roshan is 19 years old
Checking for Protocol Conformance
You can use the is and as operators to check whether an instance conforms to a protocol or to cast it to a certain type.
isThe operator is used to check whether an instanceFollowconforms to a certainProtocol。as?returns an optional value. When the instanceFollowconforms to the protocol, it returns the protocol type; otherwise, it returns nil.nil。asis used for forced downcasting. If the cast fails, it causes a runtime error.
Examples
The following example defines a protocol called HasArea, which requires a readable area of type Double:
protocol HasArea {
var area: Double { get }
}
// 定义了Circle类,都遵循了HasArea协议
class Circle: HasArea {
let pi = 3.1415927
var radius: Double
var area: Double { return pi * radius * radius }
init(radius: Double) { self.radius = radius }
}
// 定义了Country类,都遵循了HasArea协议
class Country: HasArea {
var area: Double
init(area: Double) { self.area = area }
}
// Animal是一个没有实现HasArea协议的类
class Animal {
var legs: Int
init(legs: Int) { self.legs = legs }
}
let objects: [AnyObject] = [
Circle(radius: 2.0),
Country(area: 243_610),
Animal(legs: 4)
]
for object in objects {
// 对迭代出的每一个元素进行检查,看它是否遵循了HasArea协议
if let objectWithArea = object as? HasArea {
print("面积为 \(objectWithArea.area)")
} else {
print("没有面积")
}
}
The output of the above program execution is:
面积为 12.5663708 面积为 243610.0 没有面积Other Extensions