698 Stimmen

@selector() in Swift?

Ich versuche, einen NSTimer in Swift zu erstellen, aber ich habe einige Probleme.

NSTimer(timeInterval: 1, target: self, selector: test(), userInfo: nil, repeats: true)

test() ist eine Funktion in derselben Klasse.


Ich bekomme einen Fehler im Editor:

Konnte keine Überladung für 'init' finden, die die bereitgestellten Argumente akzeptiert

Wenn ich selector: test() zu selector: nil ändere, verschwindet der Fehler.

Ich habe versucht:

  • selector: test()
  • selector: test
  • selector: Selector(test())

Aber nichts funktioniert und ich kann keine Lösung in den Referenzen finden.

963voto

rickster Punkte 121640

Swift selbst verwendet keine Selektoren - mehrere Designmuster, die in Objective-C Selektoren verwenden, funktionieren in Swift anders. (Verwenden Sie zum Beispiel optionale Verkettung bei Protokolltypen oder is/as Tests anstelle von respondsToSelector: und verwenden Sie überall Closures, wo Sie können, anstelle von performSelector: für bessere Typ-/Speichersicherheit.)

Aber es gibt immer noch eine Reihe wichtiger auf ObjC basierter APIs, die Selektoren verwenden, einschließlich Timer und dem Target/Action-Muster. Swift bietet den Typ Selector für die Arbeit mit diesen an. (Swift verwendet automatisch dies anstelle von ObjCs SEL Typ.)

In Swift 2.2 (Xcode 7.3) und später (einschließlich Swift 3 / Xcode 8 und Swift 4 / Xcode 9):

Sie können einen Selector aus einem Swift-Funktionstyp mithilfe des #selector Ausdrucks erstellen.

let timer = Timer(timeInterval: 1, target: object,
                  selector: #selector(MyClass.test),
                  userInfo: nil, repeats: false)
button.addTarget(object, action: #selector(MyClass.buttonTapped),
                 for: .touchUpInside)
view.perform(#selector(UIView.insertSubview(_:aboveSubview:)),
             with: button, with: otherButton)

Der tolle an diesem Ansatz? Eine Funktionsreferenz wird vom Swift-Compiler überprüft, sodass Sie den #selector Ausdruck nur mit Klassen/Methodenpaaren verwenden können, die tatsächlich existieren und für die Verwendung als Selektoren berechtigt sind (siehe "Selektor-Verfügbarkeit" unten). Sie können auch Ihre Funktionsreferenz nur so spezifisch machen, wie Sie benötigen, gemäß den Regeln für Funktionsnamen in Swift 2.2+.

(Dies ist tatsächlich eine Verbesserung gegenüber ObjCs @selector() Direktive, da der Compiler das Überprüfen auf nicht deklarierte Selektoren nur daraufhin überprüft, ob der benannte Selektor existiert. Die Swift-Funktionsreferenz, die Sie an #selector übergeben, prüft das Vorhandensein, die Zugehörigkeit zu einer Klasse und die Typensignatur.)

Es gibt ein paar zusätzliche Einschränkungen für die Funktionsreferenzen, die Sie an den #selector Ausdruck übergeben:

  • Mehrere Funktionen mit demselben Grundnamen können anhand ihrer Parameterbeschriftungen mit der oben genannten Syntax für Funktionsreferenzen unterschieden werden (z.B. insertSubview(_:at:) vs. insertSubview(_:aboveSubview:)). Aber wenn eine Funktion keine Parameter hat, ist der einzige Weg, sie zu unterscheiden, die Verwendung eines as Casts mit der Typsignatur der Funktion (z.B. foo as () -> () vs. foo(_:)).
  • Es gibt eine spezielle Syntax für Getter/Setter-Paare von Eigenschaften in Swift 3.0+. Zum Beispiel, bei einer var foo: Int, können Sie #selector(getter: MyClass.foo) oder #selector(setter: MyClass.foo) verwenden.

Allgemeine Anmerkungen:

Fälle, in denen #selector nicht funktioniert, und Benennung: Manchmal haben Sie keinen Funktionsverweis, um einen Selektor zu erstellen (zum Beispiel bei Methoden, die dynamisch im ObjC-Laufzeitmodus registriert sind). In diesem Fall können Sie einen Selector aus einer Zeichenkette erstellen: z.B. Selector("dynamicMethod:") - obwohl Sie dabei die Gültigkeitsprüfung des Compilers verlieren. Wenn Sie das tun, müssen Sie die ObjC-Benennungsregeln befolgen, einschließlich Doppelpunkte (:) für jeden Parameter.

Selektor-Verfügbarkeit: Die von dem Selektor referenzierte Methode muss dem ObjC-Laufzeitmodus zugänglich sein. In Swift 4 muss jede Methode, die an ObjC übergeben wird, ihre Deklaration mit dem Attribut @objc versehen. (In früheren Versionen haben Sie in einigen Fällen dieses Attribut kostenlos erhalten, aber jetzt müssen Sie es explizit deklarieren.)

Vergessen Sie nicht, dass private Symbole auch nicht dem Laufzeitmodus verfügbar sind - Ihre Methode muss mindestens eine Sichtbarkeit von internal haben.

Schlüsselpfade: Diese sind mit, aber nicht genau dasselbe wie Selektoren. Es gibt auch eine spezielle Syntax dafür in Swift 3: z.B. chris.valueForKeyPath(#keyPath(Person.friends.firstName)). Sehen Sie SE-0062 für Details. Und noch mehr KeyPath-Kram in Swift 4, also stellen Sie sicher, dass Sie die richtige KeyPath-basierte API verwenden, anstatt Selektoren, wenn es angemessen ist.

Weitere Informationen zu Selektoren finden Sie unter Interaktion mit ObjC-APIs in Verwenden von Swift mit Cocoa und Objective-C.

Hinweis: Vor Swift 2.2 entsprach Selector dem StringLiteralConvertible, daher finden Sie möglicherweise alten Code, in dem nackte Zeichenfolgen an APIs übergeben werden, die Selektoren akzeptieren. Sie möchten in Xcode "In die aktuelle Swift-Syntax konvertieren" ausführen, um sicherzustellen, dass diese #selector verwenden.

96voto

Oscar Swanros Punkte 19737

Hier ist ein schnelles Beispiel, wie man die Selector-Klasse in Swift verwendet:

override func viewDidLoad() {
    super.viewDidLoad()

    var rightButton = UIBarButtonItem(title: "Titel", style: UIBarButtonItemStyle.Plain, target: self, action: Selector("method"))
    self.navigationItem.rightBarButtonItem = rightButton
}

func method() {
    // Hier etwas Cooles machen
}

Beachten Sie, dass wenn die als String übergebene Methode nicht funktioniert, wird es zur Laufzeit scheitern und Ihre App abstürzen lassen. Seien Sie vorsichtig

48voto

user3771857 Punkte 617

Außerdem, wenn Ihre (Swift) Klasse nicht von einer Objective-C Klasse abstammt, dann muss am Ende des Zielmethodennamen-Strings ein Doppelpunkt stehen und Sie müssen die Eigenschaft @objc mit Ihrer Zielmethode verwenden z.B.

var rightButton = UIBarButtonItem(title: "Title", style: UIBarButtonItemStyle.Plain, target: self, action: Selector("method"))

@objc func method() {
    // Hier etwas Cooles    
} 

ansonsten erhalten Sie zur Laufzeit einen "Unrecognized Selector" Fehler.

37voto

Kyle Clegg Punkte 37388

Swift 2.2+ und Swift 3 Update

Verwenden Sie den neuen #selector Ausdruck, der die Verwendung von Zeichenfolgenliteralen überflüssig macht und somit weniger fehleranfällig ist. Für Referenz:

Selector("keyboardDidHide:")

wird zu

#selector(keyboardDidHide(_:))

Siehe auch: Swift Evolution Proposal

Hinweis (Swift 4.0):

Wenn Sie #selector verwenden, müssen Sie die Funktion als @objc markieren

Beispiel:

@objc func something(_ sender: UIButton)

29voto

pravin salame Punkte 349

Swift 4.0

Sie erstellen den Selector wie unten gezeigt.

1. Fügen Sie das Ereignis einem Button hinzu, wie folgt:

button.addTarget(self, action: #selector(clickedButton(sender:)), for: UIControlEvents.touchUpInside)

Und die Funktion wird wie folgt sein:

@objc func clickedButton(sender: AnyObject) {

}

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X