Protocoles Swift
Les protocoles définissent les méthodes et propriétés requises pour implémenter une fonctionnalité particulière.
Tout type qui satisfait aux exigences d'un protocole est dit conforme au protocole.
Les classes, les structures ou les types énumérés peuvent tous se conformer à des protocoles et fournir des implémentations concrètes pour satisfaire les méthodes et fonctionnalités définies par le protocole.
Les protocoles Swift sont des plans qui définissent des méthodes, des propriétés et d'autres exigences adaptées à une tâche ou à une fonctionnalité particulière. Les protocoles peuvent être adoptés par des classes, des structures et des énumérations pour fournir des implémentations concrètes de ces exigences.
Les protocoles sont une fonctionnalité très puissante du langage Swift qui permettent aux développeurs de définir des interfaces, en spécifiant les propriétés et méthodes qu’un type doit avoir, sans spécifier les implémentations concrètes de ces propriétés et méthodes.
Syntaxe
Le format syntaxique d’un protocole est le suivant :
protocol SomeProtocol {
// 这里可以定义协议要求
var someProperty: Int { get set }
func someMethod()
}
SomeProtocol définit une propriété lisible et modifiable someProperty et une méthode someMethod.
Pour qu’une classe se conforme à un protocole, ajoutez le nom du protocole après le nom du type, séparé par deux-points:dans le cadre de la définition du type. Lorsqu’elle se conforme à plusieurs protocoles, séparez chaque protocole par une virgule,séparés.
struct SomeStructure: FirstProtocol, AnotherProtocol {
// 结构体内容
}
Si une classe a une superclasse tout en se conformant à un protocole, placez le nom de la superclasse avant le nom du protocole, séparé par une virgule.
class SomeClass: SomeSuperClass, FirstProtocol, AnotherProtocol {
// 类的内容
}
Exigences relatives aux propriétés
Les protocoles sont utilisés pour spécifier des propriétés d’instance ou des propriétés de type particulières, sans préciser s’il s’agit de propriétés stockées ou calculées. De plus, vous devez indiquer si elles sont en lecture seule ou en lecture et en écriture.
Dans les protocoles, var est généralement utilisé pour déclarer des propriétés variables. Après la déclaration de type, ajoutez{ set get }pour indiquer que la propriété est lisible et modifiable. Pour les propriétés en lecture seule, utilisez{ get }pour indiquer.
Exemples
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 = "Protocoles Swift"
var stname = "Protocoles"
func attendance() -> String {
return "\(stname) a obtenu 99% de présence"
}
func markssecured() -> String {
return "\(stname) a obtenu \(marks) points"
}
}
let studdet = ClassC()
studdet.stname = "Swift"
studdet.marks = 98
print(studdet.markssecured())
print(studdet.attendance())
print("Notes : \(studdet.marks)")
print("Résultat : \(studdet.result)")
print("Présent : \(studdet.present)")
print("Matière : \(studdet.subject)")
print("Nom de l'étudiant : \(studdet.stname)")
La sortie de l'exécution du programme ci-dessus est :
Swift has scored 98 marks The Swift has secured 99% attendance Marks: 98 Result: true Present: false Subject: Swift 协议 Student Name: Swift
Exigences relatives aux méthodes mutating
Parfois, vous devez modifier une instance au sein de sa méthode.
Par exemple, dans les méthodes d'instance des types valeur (structures, énumérations), préfixez la fonction avec le mot-clé mutating avant func, ce qui indique que la méthode est autorisée à modifier l'instance à laquelle elle appartient et les valeurs de ses propriétés d'instance.
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()
La sortie de l'exécution du programme ci-dessus est :
Wednesday
Exigences d'initialiseur
Les protocoles peuvent exiger que les types conformes implémentent des initialiseurs spécifiques.
Vous pouvez écrire des déclarations d'initialiseur dans une définition de protocole comme vous écririez des initialiseurs ordinaires, mais sans les accolades ni le corps de l'initialiseur. La syntaxe est la suivante :
protocol SomeProtocol {
init(someParameter: Int)
}
Exemples
protocol tcpprotocol {
init(aprot: Int)
}
Implémentation des exigences d'initialiseur de protocole dans les classes
Vous pouvez implémenter l'initialiseur dans une classe qui se conforme au protocole, et le désigner comme initialiseur désigné ou initialiseur de commodité pour la classe. Dans les deux cas, vous devez marquer l'implémentation de l'initialiseur avec le modificateur "required" :
class SomeClass: SomeProtocol {
required init(someParameter: Int) {
// 构造器实现
}
}
protocol tcpprotocol {
init(aprot: Int)
}
class tcpClass: tcpprotocol {
required init(aprot: Int) {
}
}
L'utilisation du modificateur required garantit que toutes les sous-classes qui se conforment au protocole fournissent également une implémentation explicite ou héritent d'une implémentation pour l'exigence d'initialiseur.
Si une sous-classe redéfinit un initialiseur désigné de sa super-classe, et que cet initialiseur satisfait une exigence de protocole, l'implémentation de l'initialiseur doit être marquée avec les deux modificateurs required et override :
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)")
La sortie de l'exécution du programme ci-dessus est :
res is: 20 show.no1 is: 30 show.no2 is: 50
Les protocoles en tant que types
Bien que les protocoles n'implémentent eux-mêmes aucune fonctionnalité, ils peuvent être utilisés comme types.
Les protocoles peuvent être utilisés comme n'importe quel autre type ordinaire. Cas d'utilisation :
- Comme types de paramètres ou types de retour dans les fonctions, les méthodes ou les initialiseurs
- Comme type de constantes, variables ou propriétés
- Comme types d'éléments dans les tableaux, les dictionnaires ou d'autres conteneurs
Exemples
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 }))
La sortie de l'exécution du programme ci-dessus est :
10 20 30 5 10 15 [100, 200, 300] [10, 20, 30]
Ajout de membres de protocole avec des extensions
Nous pouvons utiliser des extensions pour étendre des types existants (classes, structures, énumérations, etc.).
Les extensions peuvent ajouter des membres tels que des propriétés, des méthodes, des indices, des protocoles, etc. à des types existants.
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())")
La sortie du code ci-dessus est :
John Doe is a Normal Jane Smith is a Elderly Baby Yoda is a Baby
Héritage de protocole
Un protocole peut hériter d'un ou plusieurs autres protocoles et peut ajouter des exigences supplémentaires en plus des protocoles hérités.
La syntaxe de l'héritage de protocole est similaire à celle de l'héritage de classe. Plusieurs protocoles hérités sont séparés par des virgules :
protocol InheritingProtocol: SomeProtocol, AnotherProtocol {
// 协议定义
}
Exemples
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)
La sortie de l'exécution du programme ci-dessus est :
学生尝试 1 次通过 学生尝试 1 次通过 学生尝试 1 次通过 学生尝试 5 次通过 学生尝试 5 次通过 学生缺席考试 学生尝试 5 次通过 学生缺席考试
Protocoles réservés aux classes
Vous pouvez restreindre un protocole aux types de classe uniquement en ajoutant le mot-clé class à sa liste d'héritage.
Le mot-clé class doit être le premier élément de la liste d'héritage du protocole, suivi des autres protocoles hérités. Le format est le suivant :
protocol SomeClassOnlyProtocol: class, SomeInheritedProtocol {
// 协议定义
}
Exemples
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)")
La sortie de l'exécution du programme ci-dessus est :
res is: 20 show.no1 is: 30 show.no2 is: 50
Composition de protocoles
Swift prend en charge la composition de plusieurs protocoles, ce qui est très utile lorsque nous devons nous conformer à plusieurs protocoles en même temps.
Le format de la syntaxe est le suivant :
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)
La sortie de l'exécution du programme ci-dessus est :
Priya is 21 years old Rehan is 29 years old Roshan is 19 years old
Vérification de la conformité à un protocole
Vous pouvez utiliser les opérateurs is et as pour vérifier si une instance se conforme à un protocole ou pour la convertir en un certain type.
isL'opérateur est utilisé pour vérifier si une instanceFollowse conforme à un certainProtocol。as?renvoie une valeur optionnelle. Lorsque l'instanceFollowse conforme au protocole, il renvoie le type du protocole ; sinon, il renvoie nil.nil。asest utilisé pour le transtypage descendant forcé. Si le transtypage échoue, il provoque une erreur d'exécution.
Exemples
L'exemple suivant définit un protocole appelé HasArea, qui requiert une propriété area accessible en lecture de 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("没有面积")
}
}
La sortie de l'exécution du programme ci-dessus est :
面积为 12.5663708 面积为 243610.0 没有面积Autres extensions