Objekt X der Klasse Y implementiert methodSignatureForSelector in Swift nicht

88

Ich habe eine Klasse Person, die mehrmals instanziiert wird. Jede Person bekommt ihren eigenen Timer. Nach meiner initfür PersonIch nenne startTimer().

class Person {
 var timer = NSTimer()
 func startTimer() {
    timer = NSTimer.scheduledTimerWithTimeInterval(1, target: self, selector: Selector("timerTick"), userInfo: nil, repeats: true)
 }

 func timerTick() {
    angerLevel++
    println("Angry! \(angerLevel)")
 }
...
...
}

Ich kann also 3 Instanzen von Person in einem Array von haben Person[]. Ich erhalte eine Fehlermeldung:

2014-06-25 13:57:14.956 ThisProgram[3842:148856] *** NSForwarding: warning: object 0x113760048 of class '_TtC11ThisProgram6Person' does not implement methodSignatureForSelector: -- trouble ahead

Ich habe an anderer Stelle gelesen, dass ich von erben sollte, NSObjectaber dies ist in Swift nicht Obj-C. Die Funktion befindet sich innerhalb der Klasse, daher bin ich mir nicht sicher, was ich tun soll.

Johnston
quelle
3
Sie haben bereits herausgefunden, dass die Klasse von NSObject erben sollte : class Person : NSObject { ... }. Suchen Sie eine andere Lösung?
Martin R

Antworten:

160

Stellen Sie sich das nicht NSObjectals Objective-C-Klasse vor, sondern als Cocoa / Foundation-Klasse. Auch wenn Sie Swift anstelle von Objective-C verwenden, verwenden Sie immer noch dieselben Frameworks.

Zwei Optionen: (1) Fügen Sie das dynamicAttribut der Funktion hinzu , auf die Sie als Selektor verweisen möchten:

    dynamic func timerTick() {
        self.angerLevel++
        print("Angry! \(self.angerLevel)")
    }

Oder (2) Personals Unterklasse von deklarieren NSObjectund dann einfach super.init()am Anfang Ihres Initialisierers aufrufen :

class Person: NSObject {
    var timer = NSTimer()
    var angerLevel = 0

    func startTimer() {
        print("starting timer")
        timer = NSTimer.scheduledTimerWithTimeInterval(1, target: self, selector: "timerTick", userInfo: nil, repeats: true)
    }

    func timerTick() {
        self.angerLevel++
        print("Angry! \(self.angerLevel)")
    }

    override init() {
        super.init()
        self.startTimer()
    }
}
Nate Cook
quelle
3
Sie sollten auch in der Lage sein, die Funktionsdeklaration so zu dekorieren @objc func timerTick(). Die NSTimer-API scheint ziemlich abhängig von der Obj-C-Laufzeit zu sein.
Macshome
Guter Anruf - zur Antwort hinzugefügt
Nate Cook
1
Danke, dass mein Problem behoben wurde. Aber kannst du erklären warum? Was braucht es den @objc Teil?
Aggressor
NSTimerVerwendet die Nachrichtenweiterleitung, um den Zielselektor aufzurufen. Dies ist eine Objective-C-Funktion, die bei Swift-Typen standardmäßig nicht behandelt wird. Wenn Sie das @objcAttribut verwenden oder von einer Objective-C-Klasse erben, entscheiden Sie sich für mehrere Funktionen, einschließlich der Nachrichtenweiterleitung.
Nate Cook
2
Keine dieser Lösungen wird mehr benötigt. Es reicht aus, die Auswahlfunktion zu deklarieren dynamic. Sie sind beide gut und beide funktionieren immer noch, aber die Verwendung dynamicdieser einen Funktion kann als leichterer Ansatz angesehen werden.
Matt
32

Seit XCode6 Beta 6 können Sie 'dynamische' Funktionen verwenden

dynamic func timerTick() { .... }
Kritsana U.
quelle
Dies löste mein Problem beim Versuch, UILocalizedIndexedCollation.currentCollation ()
DogCoffee
Dies ist ein besserer Ansatz, als die gesamte Klasse von NSObject erben zu lassen.
Bobics
8

Ich hatte einen ähnlichen Fehler beim Versuch, let encodedArchive = NSKeyedArchiver.archivedDataWithRootObject(archive) as NSDatadas Archiv als Array einer benutzerdefinierten Klasse zu verwenden. Ich fand, dass das Deklarieren dieser benutzerdefinierten Klasse als Unterklasse von NSObject und NSCoding den Trick machte. Es werden einige weitere Zeilen erforderlich sein, um dem Protokoll der NSCoding zu entsprechen, sodass es zunächst ungefähr so ​​aussieht:

class Person: NSObject, NSCoding {
  init() {
    super.init()
  }

  func encodeWithCoder(_aCoder: NSCoder) {   }
}
Jeremy Rea
quelle