Myślę, że mogę odpowiedzieć na twoje pytanie, ale nie spodoba ci się to.
TL; DR: @objc
funkcje mogą obecnie nie znajdować się w rozszerzeniach protokołu. Zamiast tego można utworzyć klasę bazową, chociaż nie jest to idealne rozwiązanie.
Rozszerzenia protokołów i Objective-C
Po pierwsze, to pytanie / odpowiedź ( Can Swift Method Defined on Extensions on Protocols Accessed in Objective-c ) wydaje się sugerować, że ze względu na sposób, w jaki rozszerzenia protokołów są wysyłane pod maską, metody zadeklarowane w rozszerzeniach protokołów nie są widoczne dla objc_msgSend()
funkcji, oraz dlatego nie są widoczne dla kodu Objective-C. Ponieważ metoda, którą próbujesz zdefiniować w swoim rozszerzeniu, musi być widoczna dla Objective-C (więc UIKit
możesz jej użyć), wrzeszczy na ciebie, że nie uwzględniasz @objc
, ale kiedy to zrobisz, wrzeszczy na ciebie, ponieważ @objc
nie jest dozwolone w rozszerzenia protokołów. Dzieje się tak prawdopodobnie dlatego, że rozszerzenia protokołów nie są obecnie widoczne dla Objective-C.
Widzimy również, że komunikat o błędzie po dodaniu @objc
stanów „@objc może być używany tylko z elementami członkowskimi klas, protokołami @objc i konkretnymi rozszerzeniami klas”. To nie jest klasa; rozszerzenie protokołu @objc nie jest tym samym, co znajdowanie się w samej definicji protokołu (tj. w wymaganiach), a słowo „konkretny” sugerowałoby, że rozszerzenie protokołu nie liczy się jako konkretne rozszerzenie klasy.
Obejście problemu
Niestety, to prawie całkowicie uniemożliwia używanie rozszerzeń protokołów, gdy domyślne implementacje muszą być widoczne dla frameworków Objective-C. Na początku pomyślałem, że być może @objc
nie jest to dozwolone w twoim rozszerzeniu protokołu, ponieważ Swift Compiler nie mógł zagwarantować, że zgodne typy będą klasami (nawet jeśli specjalnie określono UIViewController
). Więc postawiłem class
wymóg P1
. To nie zadziałało.
Być może jedynym obejściem jest po prostu użycie tutaj klasy bazowej zamiast protokołu, ale oczywiście nie jest to całkowicie idealne, ponieważ klasa może mieć tylko jedną klasę bazową, ale być zgodna z wieloma protokołami.
Jeśli zdecydujesz się pójść tą drogą, weź pod uwagę to pytanie ( metoda protokołu opcjonalnego Swift 3 ObjC nie wywołana w podklasie ). Wydaje się, że innym aktualnym problemem w Swift 3 jest to, że podklasy nie dziedziczą automatycznie implementacji wymagań protokołu opcjonalnego swojej nadklasy. Odpowiedź na te pytania wymaga specjalnego dostosowania, @objc
aby to obejść.
Zgłaszanie problemu
Myślę, że jest to już omawiane wśród osób pracujących nad projektami Swift open source, ale możesz być pewien, że są świadomi, używając narzędzia Apple Bug Reporter , który prawdopodobnie trafiłby do Swift Core Team lub do zgłaszającego błędy Swifta . Jednak w obu przypadkach błąd może być zbyt szeroki lub już znany. Zespół Swift może również rozważyć, czego szukasz jako nowej funkcji językowej, w takim przypadku powinieneś najpierw sprawdzić listy mailingowe .
Aktualizacja
W grudniu 2016 r. Ten problem został zgłoszony społeczności Swift. Sprawa jest nadal oznaczona jako otwarta ze średnim priorytetem, ale dodano następujący komentarz:
To jest zamierzone. Nie ma możliwości dodania implementacji metody do każdego adoptera, ponieważ rozszerzenie mogłoby zostać dodane po uzyskaniu zgodności z protokołem. Przypuszczam jednak, że moglibyśmy na to zezwolić, gdyby rozszerzenie znajdowało się w tym samym module co protokół.
Ponieważ Twój protokół znajduje się w tym samym module co rozszerzenie, możesz to zrobić w przyszłej wersji Swift.
Zaktualizuj 2
W lutym 2017 r. Jeden z członków Swift Core Team oficjalnie zamknął ten problem jako „Nie zrobię” z następującym komunikatem:
Jest to zamierzone: rozszerzenia protokołów nie mogą wprowadzać punktów wejścia @objc z powodu ograniczeń środowiska wykonawczego Objective-C. Jeśli chcesz dodać punkty wejścia @objc do NSObject, rozszerz NSObject.
Przedłużenie NSObject
lub nawet UIViewController
nie przyniesie dokładnie tego, czego chcesz, ale niestety nie wygląda na to, że stanie się to możliwe.
W (bardzo) długoterminowej przyszłości możemy @objc
całkowicie wyeliminować poleganie na metodach, ale ten czas prawdopodobnie nie nadejdzie w najbliższym czasie, ponieważ frameworki Cocoa nie są obecnie pisane w języku Swift (i nie mogą być, dopóki nie będą miały stabilnego ABI) .
Zaktualizuj 3
Od jesieni 2019 roku staje się to mniejszym problemem, ponieważ coraz więcej frameworków Apple jest pisanych w języku Swift. Na przykład, jeśli użyjesz SwiftUI
zamiast UIKit
, całkowicie ominiesz problem, ponieważ @objc
nigdy nie będzie to konieczne w odniesieniu do SwiftUI
metody.
Frameworki Apple napisane w Swift obejmują:
- SwiftUI
- RealityKit
- Połączyć
- CryptoKit
Można by oczekiwać, że ten wzorzec będzie się utrzymywał w czasie, teraz, gdy Swift jest oficjalnie ABI i stabilny moduł od wersji Swift 5.0 i 5.1.
@objc