Sobes.tech
Middle+

Co narusza opcjonalną funkcję w protokole?

sobes.tech AI

Odpowiedź od AI

Dodanie wymagań do protokołu po tym, jak inne moduły lub biblioteki zaczęły go używać. Jeśli protokół został już przyjęty i zaimplementowany, dodanie metod lub właściwości opcjonalnych (nieobowiązkowych) spowoduje błędy kompilacji we wszystkich miejscach, gdzie protokół jest używany, dopóki te nowe wymagania nie zostaną spełnione.

Wymagania opcjonalne pozwalają na rozszerzanie protokołów bez "psucia" istniejących implementacji. Klasy lub struktury mogą implementować tylko część metod opcjonalnych. Jest to przydatne podczas pracy z delegatami lub dodawania nowych funkcji do już ustalonego API.

Przykład wymogu opcjonalnego w protokole Objective-C (ponieważ Swift nie obsługuje @optional bez @objc):

// W Swift `@optional` używa się z `@objc protocol`
import Foundation

@objc protocol MyDelegate {
    func requiredMethod() -> Bool

    @optional func optionalMethod1()
    @optional func optionalMethod2(value: Int)
}

class MyClass {
    weak var delegate: MyDelegate?

    func doSomething() {
        if delegate?.requiredMethod() == true {
            // Wywołanie metody opcjonalnej z sprawdzeniem
            delegate?.optionalMethod1?()

            if let delegate = delegate {
                if delegate.responds(to: #selector(delegate.optionalMethod2(value:))) {
                    delegate.optionalMethod2?(value: 10)
                }
            }
        }
    }
}

W Swift, czyste protokoły (bez @objc) nie obsługują @optional. Aby mieć wymagania opcjonalne, protokół musi być oznaczony jako @objc. Implementacje takich protokołów mogą używać rozszerzeń do dodawania domyślnych implementacji metod opcjonalnych, co jest bardziej nowoczesnym podejściem w Swift.

Przykład zachowania opcjonalnego przez rozszerzenia protokołów w Swift:

// Bardziej Swiftowe podejście: rozszerzenia protokołów z domyślnymi implementacjami
protocol MyProtocol {
    func requiredMethod() -> Bool
    func optionalMethod1()
    func optionalMethod2(value: Int)
}

// Dostarczamy domyślne implementacje dla "opcjonalnych" metod
extension MyProtocol {
    func optionalMethod1() {
        // Domyślna pusta implementacja lub standardowe zachowanie
        print("Domyślna implementacja opcjonalnej metody 1")
    }

    func optionalMethod2(value: Int) {
        // Domyślna pusta implementacja
        print("Domyślna implementacja opcjonalnej metody 2 z wartością: \(value)")
    }
}

class MyClassSwift {
    var delegate: MyProtocol?

    func doSomething() {
        if delegate?.requiredMethod() == true {
            // Wywołujemy metody bez sprawdzania na nil, ponieważ
            // albo są zaimplementowane w klasie/strukturze, albo
            // korzystają z implementacji z rozszerzenia
            delegate?.optionalMethod1()
            delegate?.optionalMethod2(value: 20)
        }
    }
}

class MyCustomDelegate: MyProtocol {
    func requiredMethod() -> Bool {
        print("Wymagana metoda zaimplementowana")
        return true
    }

    // optionalMethod1() i optionalMethod2(value:) nie muszą być zaimplementowane,
    // domyślna implementacja z rozszerzenia będzie używana.
    // Można je nadpisać, jeśli potrzebne jest inne zachowanie:
    // func optionalMethod1() {
    //     print("Niestandardowa implementacja opcjonalnej metody 1")
    // }
}

let customDelegate = MyCustomDelegate()
let myClassSwift = MyClassSwift()
myClassSwift.delegate = customDelegate
myClassSwift.doSomething()

W ten sposób dodanie opcjonalnej funkcji do protokołu narusza kompatybilność wsteczną, jeśli próbujesz dodać nowy obowiązkowy wymóg do już istniejącego protokołu, który jest używany w bazie kodu. Użycie wymagań opcjonalnych (przez @objc lub, bardziej zalecane, przez rozszerzenia protokołów) zapobiega temu naruszeniu.