Swift: Protokoły

Opublikowano: 09.11.2023 - tagi: Swift Klasa Struktura

Protokoły w Swift

W Swift protokoły działają w podobny sposób, jak interfejsy w innych językach programowania.

Za pomocą protokołu możesz rozszerzyć swoją klasę, strukturę lub enumerację o nowe właściwości i metody.

W przypadku klasy jest to przydatne, ponieważ klasę w Swift można dziedziczyć, tylko po jednej klasie.

Jeśli chodzi o struktury, tutaj jest większy problem. Struktury nie można rozszerzyć poprzez dziedziczenie. W tym przypadku ten problem rozwiązują protokoły.

Definicja protokołu

Protokół stworzysz za pomocą słowa kluczowego protocol:

protocol Fly {
    var maximumSpeed: Int { get }
	
    func fly()
}

Ponieważ protokół jest typem, jego nazwa musi zaczynać się od dużej litery.

W protokole umieszcza tylko się definicje właściwości i metod.

Składnia deklaracji właściwości wygląda tak:

var someProperty: Type { get set }

get i set służą do poinformowania, że dana zmienna jest tylko do: odczytu (get), zapisu (set) lub zarówno do odczytu jak i zapisu (get set).

Jeśli oznaczysz właściwość jako get i set:

protocol Fly {
  var maximumSpeed: Int { get set }
}

I zastosujesz ten protokół, w taki sposób, że ta właściwość będzie computed property, zostanie zgłoszony błąd:

struct Bird: Fly {
    var energy: Int = 100
    var maximumSpeed: Int {  // Błąd!
        return energy + 50
    }
}

Jest tak dlatego, bo maximumSpeed zostało oznaczone jako set, a do computed property nie można przypisać wartości.

Implementacja protokołu

Zastosowanie protokołu wygląda tak samo, jak dziedziczenie:

class Bird: Fly {
	var maximumSpeed: Int = 50
	
	func fly() {
		print("Bird is flying with maximum speed: \(maximumSpeed)")
	}
}

class Plane: Fly {
	var maximumSpeed: Int = 1000
	
	func fly() {
		print("Plane is flying with maximum speed: \(maximumSpeed)")
	}
}

var bird = Bird()
bird.fly() // Bird is flying with maximum speed: 50

var plane = Plane()
plane.fly() // Plane is flying with maximum speed: 1000

Wiele protokołów

Swift pozwalana na zastosowanie wielu protokołów:

protocol Fly {	
	func fly()
}

protocol Eat {
	func eat()
}

class Bird: Fly, Eat {	
	func fly() {
		print("Bird is flying")
	}
	
	func eat() {
		print("Bird is eating")
	}
}

var bird = Bird()
bird.fly() // Bird is flying 
bird.eat() // Bird is eating

Dziedziczenie protokołów

Możesz także dziedziczyć po... innym protokole:

protocol A {
	func doA()
}

protocol SomeProtocol: A {
	func doSomething()
}

class MyClass: SomeProtocol {
	func doA() {
		print("Call doA")
	}
	
	func doSomething() {
		print("Call doSomething")
	}
}

Istnieje możliwość dziedziczenia po wielu protokołach:

protocol A {
	func doA()
}

protocol SomeProtocol: A {
	func doSomething()
}

class MyClass: SomeProtocol {
	func doA() {
		print("Call doA")
	}
	
	func doSomething() {
		print("Call doSomething")
	}
}

Struktury i protokoły

Jeśli zamierzasz zastosować protokół do struktury, a ten posiada metodę, której implementacja w strukturze będzie działać na zasadzie zmiany stanu struktury, musisz użyć dla tej metody słowa kluczowego mutating.

Przykład:

protocol Counter {
    var counter: Int { get }
	mutating func increment()
	mutating func decrement()
}
struct MyCounter: Counter {
	var counter = 0
	
	mutating func increment() {
		counter += 1
	}
	
	mutating func decrement() {
	    counter -= 1
	}
}
var myCounter = MyCounter()
myCounter.increment()
myCounter.increment()
myCounter.decrement()

print("Counter: \(myCounter.counter)")