El objeto X de la clase Y no implementa methodSignatureForSelector en Swift

89

Tengo una persona de clase que se instancia varias veces.Cada persona tiene su propio temporizador. Sobre en mi initporque Personllamo 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)")
 }
...
...
}

Entonces puedo tener 3 instancias de Person en una matriz de Person[]. Recibo un error:

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

Leí en otra parte que debería heredar, NSObjectpero esto está en Swift, no en Obj-C. La función está dentro de la clase, así que no estoy seguro de qué hacer.

Johnston
fuente
4
Ya dimos cuenta de que la clase debe heredar de NSObject: class Person : NSObject { ... }. ¿Buscas una solución diferente?
Martin R

Respuestas:

160

No pienses NSObjecten una clase de Objective-C, piensa en ella como una clase Cocoa / Foundation. Aunque esté usando Swift en lugar de Objective-C, todavía está usando los mismos marcos.

Dos opciones: (1) agregue el dynamicatributo a la función a la que desea hacer referencia como selector:

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

O (2) declare Personcomo una subclase de NSObject, luego simplemente llame super.init()al comienzo de su inicializador:

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
fuente
3
También debería poder decorar la declaración de función de esta manera @objc func timerTick(). La API de NSTimer parece depender bastante del tiempo de ejecución de Obj-C.
macshome
Buena llamada - agregada a la respuesta
Nate Cook
1
Gracias, esto solucionó mi problema. ¿Pero puedes explicar por qué? ¿Qué necesita la parte @objc?
Agresor
NSTimerutiliza el reenvío de mensajes para llamar al selector de destino, que es una función de Objective-C que no se maneja en los tipos Swift de forma predeterminada. Cuando usa el @objcatributo o hereda de una clase Objective-C, está optando por varias funciones, incluido el reenvío de mensajes.
Nate Cook
2
Ninguna de estas soluciones ya es necesaria. Basta con declarar la función de selector dynamic. Ambos son buenos y aún funcionan, pero el uso dynamicde esta función puede verse como un enfoque más ligero.
Matt
32

Desde XCode6 beta 6, puede usar la función 'dinámica'

dynamic func timerTick() { .... }
Kritsana U.
fuente
esto resolvió mi problema al intentar usar UILocalizedIndexedCollation.currentCollation ()
DogCoffee
Este es un mejor enfoque que hacer que toda la clase herede de NSObject.
bobics
8

Tuve un error similar al intentar usar let encodedArchive = NSKeyedArchiver.archivedDataWithRootObject(archive) as NSDatadonde el archivo era una matriz de una clase personalizada. Descubrí que declarar esa clase personalizada como una subclase de NSObject y NSCoding funcionó. Requerirá algunas líneas más para cumplir con el protocolo de NSCoding, por lo que se verá así para empezar:

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

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