Przegląd:
- Mam protokół P1, który zapewnia domyślną implementację jednej z opcjonalnych funkcji Objective-C.
- Kiedy podam domyślną implementację opcjonalnej funkcji, pojawia się ostrzeżenie
Ostrzeżenie kompilatora:
Non-'@objc' method 'presentationController(_:viewControllerForAdaptivePresentationStyle:)' does not satisfy optional requirement of '@objc' protocol 'UIAdaptivePresentationControllerDelegate'
Wersja:
- Szybki: 3
- Xcode: 8 (wydanie publiczne)
Podjęte próby:
- Próbowałem dodać,
@objc
ale to nie pomaga
Pytanie:
- Jak mam to rozwiązać?
- Czy jest w pobliżu praca ?
Kod:
@objc protocol P1 : UIAdaptivePresentationControllerDelegate {
}
extension P1 where Self : UIViewController {
func presentationController(_ controller: UIPresentationController, viewControllerForAdaptivePresentationStyle style: UIModalPresentationStyle) -> UIViewController? {
return UIViewController()
}
}
class A : UIViewController, P1 {
}
swift
swift3
swift-protocols
user1046037
źródło
źródło
@objc
Odpowiedzi:
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ęcUIKit
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ślonoUIViewController
). Więc postawiłemclass
wymógP1
. 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:
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:
Przedłużenie
NSObject
lub nawetUIViewController
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
zamiastUIKit
, całkowicie ominiesz problem, ponieważ@objc
nigdy nie będzie to konieczne w odniesieniu doSwiftUI
metody.Frameworki Apple napisane w Swift obejmują:
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.
źródło
Swift 4
nie ma innej alternatywy.Właśnie natknąłem się na to po włączeniu „stabilności modułu” (włączenie „Buduj biblioteki do dystrybucji”) w szybkim środowisku, którego używam.
To, co miałem, było takie:
Funkcja w rozszerzeniu miała następujące błędy:
Metoda instancji „@objc” jako rozszerzenie podklasy „LessAwesomeClass” wymaga systemu iOS 13.0.0
Non - metoda „@ objc” „niceDelegateFunc” nie spełnia wymagań protokołu „@objc” „GreatDelegate”
Przeniesienie funkcji do klasy zamiast do rozszerzenia rozwiązało problem.
źródło
Oto inne obejście. Również napotkałem ten problem i nie mogę jeszcze przełączyć się z UIKit na SwiftUI. Przeniesienie domyślnych implementacji do wspólnej klasy bazowej również nie było dla mnie opcją. Moje domyślne implementacje były dość obszerne, więc naprawdę nie chciałem, aby cały ten kod był powielany. Obejściem, które ostatecznie zastosowałem, było użycie funkcji opakowujących w protokole, a następnie po prostu wywołanie tych funkcji z każdej klasy. Niezbyt ładne, ale w zależności od sytuacji może być lepsze niż alternatywa. Twój kod wyglądałby wtedy mniej więcej tak:
źródło