Swift-Protokolle
Protokolle definieren die Methoden und Eigenschaften, die zur Implementierung einer bestimmten Funktionalität erforderlich sind.
Man sagt, dass jeder Typ, der die Anforderungen eines Protokolls erfüllt, dem Protokoll entspricht.
Klassen, Strukturen oder Enum-Typen können alle Protokollen entsprechen und konkrete Implementierungen bereitstellen, um die vom Protokoll definierten Methoden und Funktionalitäten zu erfüllen.
Swift-Protokolle sind Blaupausen, die Methoden, Eigenschaften und andere Anforderungen definieren, die für eine bestimmte Aufgabe oder Funktion geeignet sind. Protokolle können von Klassen, Strukturen und Enums übernommen werden, um konkrete Implementierungen dieser Anforderungen bereitzustellen.
Protokolle sind ein sehr mächtiges Feature in der Sprache Swift, das es Entwicklern ermöglicht, Schnittstellen zu definieren und die Eigenschaften und Methoden anzugeben, die ein Typ haben muss, ohne die konkreten Implementierungen dieser Eigenschaften und Methoden anzugeben.
Syntax
Das Syntaxformat eines Protokolls ist wie folgt:
protocol SomeProtocol {
// 这里可以定义协议要求
var someProperty: Int { get set }
func someMethod()
}
SomeProtocol definiert eine lesbare und schreibbare Eigenschaft someProperty und eine Methode someMethod.
Um eine Klasse dazu zu bringen, einem Protokoll zu entsprechen, fügen Sie den Protokollnamen nach dem Typnamen hinzu, getrennt durch einen Doppelpunkt:als Teil der Typdefinition. Wenn sie mehreren Protokollen entspricht, trennen Sie jedes Protokoll durch ein Komma,getrennt.
struct SomeStructure: FirstProtocol, AnotherProtocol {
// 结构体内容
}
Wenn eine Klasse eine Oberklasse hat und gleichzeitig ein Protokoll erfüllt, setzen Sie den Namen der Oberklasse vor den Protokollnamen, getrennt durch ein Komma.
class SomeClass: SomeSuperClass, FirstProtocol, AnotherProtocol {
// 类的内容
}
Eigenschaftsanforderungen
Protokolle werden verwendet, um bestimmte Instanzeigenschaften oder Typ-Eigenschaften anzugeben, ohne anzugeben, ob sie gespeicherte oder berechnete Eigenschaften sind. Darüber hinaus müssen Sie angeben, ob sie schreibgeschützt oder lesbar und schreibbar sind.
In Protokollen wird var normalerweise verwendet, um variable Eigenschaften zu deklarieren. Nach der Typdeklaration fügen Sie{ set get }hinzu, um anzugeben, dass die Eigenschaft lesbar und schreibbar ist. Für schreibgeschützte Eigenschaften verwenden Sie{ get }um anzugeben.
Beispiele
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-Protokolle"
var stname = "Protokolle"
func attendance() -> String {
return "\(stname) hat 99% Anwesenheit erreicht"
}
func markssecured() -> String {
return "\(stname) hat \(marks) Punkte erreicht"
}
}
let studdet = ClassC()
studdet.stname = "Swift"
studdet.marks = 98
print(studdet.markssecured())
print(studdet.attendance())
print("Noten: \(studdet.marks)")
print("Ergebnis: \(studdet.result)")
print("Anwesend: \(studdet.present)")
print("Fach: \(studdet.subject)")
print("Studentenname: \(studdet.stname)")
Die Ausgabe der obigen Programmausführung ist:
Swift has scored 98 marks The Swift has secured 99% attendance Marks: 98 Result: true Present: false Subject: Swift 协议 Student Name: Swift
Anforderungen an mutierende Methoden
Manchmal müssen Sie eine Instanz innerhalb ihrer Methode ändern.
Zum Beispiel wird bei Instanzmethoden von Werttypen (Structs, Enums) der Funktion das Schlüsselwort mutating vor func vorangestellt, wodurch angegeben wird, dass die Methode die Instanz, zu der sie gehört, und die Werte ihrer Instanzeigenschaften ändern darf.
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()
Die Ausgabe der obigen Programmausführung ist:
Wednesday
Anforderungen an Initialisierer
Protokolle können von konformen Typen verlangen, bestimmte Initialisierer zu implementieren.
In einer Protokolldefinition können Initialisierer-Deklarationen genauso geschrieben werden wie gewöhnliche Initialisierer, jedoch ohne geschweifte Klammern und ohne den Initialisierer-Rumpf. Die Syntax lautet wie folgt:
protocol SomeProtocol {
init(someParameter: Int)
}
Beispiele
protocol tcpprotocol {
init(aprot: Int)
}
Implementieren von Initialisiereranforderungen aus Protokollen in Klassen
Sie können den Initialisierer in einer Klasse implementieren, die das Protokoll erfüllt, und ihn entweder als designierten Initialisierer oder als Convenience-Initialisierer der Klasse festlegen. In beiden Fällen müssen Sie die Initialisiererimplementierung mit dem Modifizierer "required" kennzeichnen:
class SomeClass: SomeProtocol {
required init(someParameter: Int) {
// 构造器实现
}
}
protocol tcpprotocol {
init(aprot: Int)
}
class tcpClass: tcpprotocol {
required init(aprot: Int) {
}
}
Die Verwendung des required-Modifizierers stellt sicher, dass alle Unterklassen, die das Protokoll erfüllen, ebenfalls eine explizite Implementierung bereitstellen oder eine Implementierung für die Initialisiereranforderung erben.
Wenn eine Unterklasse einen designierten Initialisierer ihrer Oberklasse überschreibt und dieser Initialisierer eine Protokollanforderung erfüllt, muss die Initialisiererimplementierung sowohl mit dem required- als auch mit dem override-Modifizierer gekennzeichnet werden:
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)")
Die Ausgabe der obigen Programmausführung ist:
res is: 20 show.no1 is: 30 show.no2 is: 50
Protokolle als Typen
Obwohl Protokolle selbst keine Funktionalität implementieren, können sie als Typen verwendet werden.
Protokolle können wie jeder andere gewöhnliche Typ verwendet werden. Anwendungsfälle:
- Als Parametertypen oder Rückgabetypen in Funktionen, Methoden oder Initialisierern
- Als Typ von Konstanten, Variablen oder Eigenschaften
- Als Elementtypen in Arrays, Dictionaries oder anderen Containern
Beispiele
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 }))
Die Ausgabe der obigen Programmausführung ist:
10 20 30 5 10 15 [100, 200, 300] [10, 20, 30]
Hinzufügen von Protokollmitgliedern mit Erweiterungen
Wir können Erweiterungen verwenden, um bestehende Typen (Klassen, Structs, Enums usw.) zu erweitern.
Erweiterungen können bestehenden Typen Mitglieder wie Eigenschaften, Methoden, Subscripts, Protokolle usw. hinzufügen.
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())")
Die Ausgabe des obigen Codes ist:
John Doe is a Normal Jane Smith is a Elderly Baby Yoda is a Baby
Protokollvererbung
Ein Protokoll kann von einem oder mehreren anderen Protokollen erben und zusätzlich zu den geerbten Protokollen weitere Anforderungen hinzufügen.
Die Syntax für Protokollvererbung ähnelt der Klassenvererbung. Mehrere geerbte Protokolle werden durch Kommas getrennt:
protocol InheritingProtocol: SomeProtocol, AnotherProtocol {
// 协议定义
}
Beispiele
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)
Die Ausgabe der obigen Programmausführung ist:
学生尝试 1 次通过 学生尝试 1 次通过 学生尝试 1 次通过 学生尝试 5 次通过 学生尝试 5 次通过 学生缺席考试 学生尝试 5 次通过 学生缺席考试
Nur-Klassen-Protokolle
Du kannst ein Protokoll auf Klassentypen beschränken, indem du das Schlüsselwort class zu seiner Vererbungsliste hinzufügst.
Das Schlüsselwort class muss das erste Element in der Vererbungsliste des Protokolls sein, gefolgt von den anderen geerbten Protokollen. Das Format ist wie folgt:
protocol SomeClassOnlyProtocol: class, SomeInheritedProtocol {
// 协议定义
}
Beispiele
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)")
Die Ausgabe der obigen Programmausführung ist:
res is: 20 show.no1 is: 30 show.no2 is: 50
Protokollzusammensetzung
Swift unterstützt die Komposition mehrerer Protokolle, was sehr nützlich ist, wenn wir gleichzeitig mehreren Protokollen entsprechen müssen.
Das Syntaxformat lautet wie folgt:
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)
Die Ausgabe der obigen Programmausführung ist:
Priya is 21 years old Rehan is 29 years old Roshan is 19 years old
Überprüfung der Protokollkonformität
Sie können die Operatoren is und as verwenden, um zu überprüfen, ob eine Instanz einem Protokoll entspricht, oder um sie in einen bestimmten Typ umzuwandeln.
isDer Operator wird verwendet, um zu überprüfen, ob eine InstanzFolloweinem bestimmten entsprichtProtocol。as?gibt einen optionalen Wert zurück. Wenn die InstanzFollowdem Protokoll entspricht, gibt sie den Protokolltyp zurück; andernfalls gibt sie nil zurück.nil。aswird für erzwungenes Downcasting verwendet. Wenn die Umwandlung fehlschlägt, verursacht sie einen Laufzeitfehler.
Beispiele
Das folgende Beispiel definiert ein Protokoll namens HasArea, das eine lesbare Eigenschaft area vom Typ Double erfordert:
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("没有面积")
}
}
Die Ausgabe der obigen Programmausführung ist:
面积为 12.5663708 面积为 243610.0 没有面积Andere Erweiterungen