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:
protocolFly{
var maximumSpeed: Int { get }
funcfly()
}
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 { getset }
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 (getset).
Jeśli oznaczysz właściwość jako get i set:
protocolFly{
var maximumSpeed: Int { getset }
}
I zastosujesz ten protokół, w taki sposób, że ta właściwość będzie computed property, zostanie zgłoszony błąd:
structBird: Fly{
var energy: Int = 100var 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:
classBird: Fly{
var maximumSpeed: Int = 50funcfly() {
print("Bird is flying with maximum speed: \(maximumSpeed)")
}
}
classPlane: Fly{
var maximumSpeed: Int = 1000funcfly() {
print("Plane is flying with maximum speed: \(maximumSpeed)")
}
}
var bird = Bird()
bird.fly() // Bird is flying with maximum speed: 50var plane = Plane()
plane.fly() // Plane is flying with maximum speed: 1000
Wiele protokołów
Swift pozwalana na zastosowanie wielu protokołów:
protocolFly{
funcfly()
}
protocolEat{
funceat()
}
classBird: Fly, Eat{
funcfly() {
print("Bird is flying")
}
funceat() {
print("Bird is eating")
}
}
var bird = Bird()
bird.fly() // Bird is flying
bird.eat() // Bird is eating
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:
protocolCounter{
var counter: Int { get }
mutatingfuncincrement()mutatingfuncdecrement()
}
Przy takiej próbie zostanie zgłoszony błąd. final możesz użyć także do właściwości klasy.
Klasa to typ referencyjny
Każdy obiekt klasy to typ referencyjny. To znaczy, że zmienna, do której przypisany jest obiekt, zawiera referencję do instancji, a nie samą instancję.
Do zmiennej user2 przypisany został obiekt ze zmiennej user1. A tak naprawdę do user2 została przypisana referencja do obiektu, którą przechowuje user1. Więc zmiana danych poprzez user1 spowoduje, że user2 będzie wskazywać na te same dane.
Swift pozwala na obserwowanie ewentualnych zmian przypisywanych do zmiennej. Dzięki property observers możesz zareagować na przypisanie wartości zmiennej tuż przed lub zaraz *po.
Możesz użyć:
willSet — Zostanie wywołane zaraz przed przypisanie nowej wartości
didSet — Zostanie wywołane tuż po przypisaniu wartości
Składania prezentuje się następująco:
var someProperty: Int = 0 {
willSet {
print("New value: \(newValue)")
}
didSet {
print("Old value: \(oldValue)")
}
}
Zarówno willSet jak i didSet przyjmują domyślnie parametry:
Dla willSet jest to newValue — jest to wartość, która ma zostać przypisana do zmiennej.
Dla didSet jest to oldValue — jest to poprzednia wartość, którą zawierała ta zmienna.
Możesz też sam określić nazwy tych parametrów:
var someProperty: Int = 0 {
willSet(valueToSet) {
print("New value: \(valueToSet)")
}
didSet(previousValue) {
print("Old value: \(previousValue)")
}
}
Przykład
structCaloriesCounter{
var totalCalories: Int = 0 {
willSet(newTotalCalories) {
print("Total calories is \(newTotalCalories)")
}
didSet {
if totalCalories > oldValue {
print("Added \(totalCalories - oldValue) calories")
}
}
}
}
Warto wiedzieć
Jeśli oznaczysz zmienną jako property observer:
Nie może to być stała let, tylko var.
Musi zawierać wartość domyślną. Jeśli nie wiesz, co przypisać przypisz nil. Możesz też oznaczyć zmienną jako optional — wtedy domyślnie zostanie przypisane nil do zmiennej.
Nie możesz użyć tego wspólnie z computed property. Ponieważ computed property nie przechowuje wartości.
Nie musisz używać willSet i didSet razem. Jeśli potrzebujesz tylko jednej z nich, użyj tylko tej.