[ 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).