L'oggetto X di classe Y non implementa methodSignatureForSelector in Swift


89

Ho una classe Persona che viene istanziata più volte. Ogni persona ha il proprio timer. Al momento nel mio initper Personio chiamo 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)")
 }
...
...
}

Quindi potrei avere 3 istanze di Persona in una matrice di Person[]. Ricevo un errore:

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

Ho letto altrove da cui dovrei ereditare, NSObjectma questo è in Swift non in Obj-C. La funzione è all'interno della classe quindi non sono sicuro di cosa fare.


4
È già capito che la classe dovrebbe ereditare da NSObject: class Person : NSObject { ... }. Cerchi una soluzione diversa?
Martin R

Risposte:


160

Non pensare NSObjecta una classe Objective-C, pensala come una classe Cocoa / Foundation. Anche se stai usando Swift invece di Objective-C, stai ancora usando tutti gli stessi framework.

Due opzioni: (1) aggiungi l' dynamicattributo alla funzione a cui vuoi fare riferimento come selettore:

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

Oppure (2) dichiarare Person come una sottoclasse diNSObject , quindi chiama semplicemente super.init()all'inizio del tuo inizializzatore:

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()
    }
}

3
Dovresti anche essere in grado di decorare la dichiarazione della funzione in questo modo @objc func timerTick(). L'API NSTimer sembra essere abbastanza dipendente dal runtime Obj-C.
macshome

Buona chiamata - aggiunta alla risposta
Nate Cook

1
Grazie questo ha RISOLTO il mio problema. Ma puoi spiegare perché? Di cosa ha bisogno la parte @objc?
Aggressore

NSTimerutilizza l'inoltro dei messaggi per chiamare il selettore mirato, che è una funzionalità Objective-C non gestita per impostazione predefinita sui tipi Swift. Quando si utilizza l' @objcattributo o si eredita da una classe Objective-C, si scelgono diverse funzionalità, tra cui l'inoltro dei messaggi.
Nate Cook

2
Nessuna di queste soluzioni è più necessaria. È sufficiente dichiarare la funzione selettore dynamic. Sono entrambi buoni ed entrambi funzionano ancora, ma l'utilizzo di dynamicquesta funzione può essere visto come un approccio più leggero.
matt

32

A partire da XCode6 beta 6, puoi usare la funzione "dinamica"

dynamic func timerTick() { .... }

questo ha risolto il mio problema cercando di utilizzare UILocalizedIndexedCollation.currentCollation ()
DogCoffee

Questo è un approccio migliore rispetto a far ereditare l'intera classe da NSObject.
bobics

8

Ho avuto un errore simile cercando di utilizzare let encodedArchive = NSKeyedArchiver.archivedDataWithRootObject(archive) as NSDatadove l'archivio era un array di una classe personalizzata. Ho scoperto che dichiarare quella classe personalizzata come una sottoclasse di NSObject e NSCoding ha funzionato. Richiederà ancora qualche riga per conformarsi al protocollo di NSCoding, quindi sarà simile a questo per iniziare:

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

  func encodeWithCoder(_aCoder: NSCoder) {   }
}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.