[ UWAGA Ta odpowiedź została pierwotnie sformułowana w języku Swift 2.2. Został poprawiony dla Swift 4, obejmując dwie ważne zmiany językowe: pierwszy parametr metody, zewnętrzny, nie jest już automatycznie pomijany, a selektor musi być jawnie uwidoczniony w Objective-C.]
Możesz obejść ten problem, rzutując odwołanie do funkcji na poprawną sygnaturę metody:
let selector = #selector(test as () -> Void)
(Jednak moim zdaniem nie powinieneś tego robić. Uważam tę sytuację za błąd, ujawniając, że składnia Swifta do odwoływania się do funkcji jest nieodpowiednia. Złożyłem raport o błędzie, ale bezskutecznie.)
Podsumowując nową #selector
składnię:
Celem tej składni jest zapobieganie zbyt częstym awariom środowiska wykonawczego (zazwyczaj „nierozpoznanym selektorowi”), które mogą wystąpić podczas dostarczania selektora jako literału. #selector()
pobiera odwołanie do funkcji , a kompilator sprawdzi, czy funkcja naprawdę istnieje i rozwiąże za Ciebie odwołanie do selektora celu-C. Dlatego nie możesz łatwo popełnić żadnego błędu.
( EDYCJA: Okay, tak, możesz. Możesz być kompletnym lunkheadem i ustawić cel na instancję, która nie implementuje komunikatu akcji określonego przez #selector
. Kompilator cię nie zatrzyma i wyłączysz się tak jak w stare dobre czasy. Ech ...)
Odwołanie do funkcji może mieć jedną z trzech form:
Sama nazwa funkcji. Jest to wystarczające, jeśli funkcja jest jednoznaczna. Tak więc na przykład:
@objc func test(_ sender:AnyObject?) {}
func makeSelector() {
let selector = #selector(test)
}
Jest tylko jedna test
metoda, więc #selector
odnosi się do niej, mimo że przyjmuje parametr, a parametr #selector
nie wspomina. Rozwiązany selektor celu-C, za kulisami, nadal będzie poprawnie wyglądał "test:"
(z dwukropkiem wskazującym parametr).
Nazwa funkcji wraz z resztą jej podpisu . Na przykład:
func test() {}
func test(_ sender:AnyObject?) {}
func makeSelector() {
let selector = #selector(test(_:))
}
Mamy dwie test
metody, więc musimy rozróżnić; notacja test(_:)
sprowadza się do drugiej , tej z parametrem.
Nazwa funkcji z resztą podpisu lub bez niej, a także rzutowanie pokazujące typy parametrów. A zatem:
@objc func test(_ integer:Int) {}
@nonobjc func test(_ string:String) {}
func makeSelector() {
let selector1 = #selector(test as (Int) -> Void)
let selector2 = #selector(test(_:) as (Int) -> Void)
}
Tutaj mamy przeciążenie test(_:)
. Przeciążenie nie może być wystawione na Objective-C, ponieważ Objective-C nie zezwala na przeciążanie, więc tylko jeden z nich jest ujawniony i możemy utworzyć selektor tylko dla tego, który jest ujawniony, ponieważ selektory są funkcją Objective-C . Ale nadal musimy ujednoznacznić, jeśli chodzi o Swifta, a obsada to robi.
(To właśnie ta cecha językowa jest używana - moim zdaniem nadużywana - jako podstawa powyższej odpowiedzi).
Być może będziesz musiał pomóc Swift w rozwiązaniu odwołania do funkcji, wskazując mu, w jakiej klasie znajduje się funkcja:
Jeśli klasa jest taka sama jak ta lub w górę łańcucha nadklasy od tej klasy, zwykle nie jest potrzebne dalsze rozwiązanie (jak pokazano w przykładach powyżej); opcjonalnie możesz powiedzieć self
, używając notacji kropkowej (np. #selector(self.test)
w niektórych sytuacjach może być to konieczne.
W przeciwnym razie możesz użyć odwołania do instancji, dla której metoda jest zaimplementowana, z notacją kropkową, jak w tym prawdziwym przykładzie ( self.mp
jest to MPMusicPlayerController):
let pause = UIBarButtonItem(barButtonSystemItem: .pause,
target: self.mp, action: #selector(self.mp.pause))
... lub możesz użyć nazwy klasy z notacją kropkową:
class ClassA : NSObject {
@objc func test() {}
}
class ClassB {
func makeSelector() {
let selector = #selector(ClassA.test)
}
}
(Wydaje się, że jest to ciekawa notacja, ponieważ wygląda na to, że mówisz, że test
jest to metoda klasowa, a nie metoda instancji, ale mimo to zostanie poprawnie rozwiązana do selektora, i tylko to się liczy).
as
let selector = #selector(test as (Void) -> Void)
.test as (Void) -> Void
czy krótsza składniatest as () -> ()
?Chcę dodać brakujące ujednoznacznienie: dostęp do metody instancji spoza klasy.
class Foo { @objc func test() {} @objc func test(_ sender: AnyObject?) {} }
Z punktu widzenia klasy pełna sygnatura
test()
metody to(Foo) -> () -> Void
, którą należy określić, aby uzyskać plikSelector
.#selector(Foo.test as (Foo) -> () -> Void) #selector(Foo.test(_:))
Alternatywnie możesz odwołać się do instancji,
Selector
jak pokazano w oryginalnej odpowiedzi.let foo = Foo() #selector(foo.test as () -> Void) #selector(foo.test(_:))
źródło
Foo.xxx
jest już dziwna, ponieważ nie są to zewnętrzne metody klasowe. Wygląda więc na to, że kompilator daje ci przepustkę, ale tylko wtedy, gdy nie ma dwuznaczności. Jeśli pojawia się niejasność, musisz cofnąć rękawy i użyć dłuższej notacji, która jest legalna i dokładna, ponieważ metoda instancji jest „potajemnie” metodą klas curried. Bardzo dokładne wykrywanie pozostałej krawędzi!W moim przypadku (Xcode 11.3.1) błąd występował tylko podczas używania lldb podczas debugowania. Podczas pracy działa poprawnie.
źródło