L'objet X de la classe Y n'implémente pas methodSignatureForSelector dans Swift

89

J'ai une classe Person qui est instanciée plusieurs fois. Chaque personne a sa propre minuterie. Sur mon initpour Personj'appelle 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)")
 }
...
...
}

Je peux donc avoir 3 instances de Person dans un tableau de Person[]. J'obtiens une erreur:

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

J'ai lu ailleurs que je devrais hériter NSObjectmais c'est dans Swift pas Obj-C. La fonction est dans la classe donc je ne sais pas quoi faire.

Johnston
la source
4
Vous avez déjà compris que la classe doit hériter de NSObject: class Person : NSObject { ... }. Cherchez-vous une solution différente?
Martin R

Réponses:

160

Ne pensez pas NSObjectà une classe Objective-C, pensez-y comme à une classe Cocoa / Foundation. Même si vous utilisez Swift au lieu d'Objective-C, vous utilisez toujours les mêmes frameworks.

Deux options: (1) ajoutez l' dynamicattribut à la fonction que vous souhaitez référencer en tant que sélecteur:

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

Ou (2) déclarez Personcomme une sous-classe de NSObject, puis appelez simplement super.init()au début de votre initialiseur:

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
la source
3
Vous devriez également pouvoir décorer la déclaration de fonction comme ceci @objc func timerTick(). L'API NSTimer semble être assez dépendante du Runtime Obj-C.
macshome
Bon appel - ajouté à la réponse
Nate Cook
1
Merci ce FIXED mon problème. Mais pouvez-vous expliquer pourquoi? De quoi a-t-il besoin pour la partie @objc?
Aggressor
NSTimerutilise le transfert de message pour appeler le sélecteur ciblé, qui est une fonctionnalité Objective-C non gérée par défaut sur les types Swift. Lorsque vous utilisez l' @objcattribut ou héritez d'une classe Objective-C, vous optez pour plusieurs fonctionnalités, notamment le transfert de message.
Nate Cook
2
Aucune de ces solutions n'est plus nécessaire. Il suffit de déclarer la fonction de sélection dynamic. Ils sont tous les deux bons et fonctionnent toujours tous les deux, mais l'utilisation dynamicde cette fonction peut être considérée comme une approche plus légère.
mat
32

Depuis XCode6 beta 6, vous pouvez utiliser la fonction 'dynamique'

dynamic func timerTick() { .... }
Kritsana U.
la source
cela a résolu mon problème en essayant d'utiliser UILocalizedIndexedCollation.currentCollation ()
DogCoffee
C'est une meilleure approche que de faire hériter la classe entière de NSObject.
bobics
8

J'ai eu une erreur similaire en essayant d'utiliser let encodedArchive = NSKeyedArchiver.archivedDataWithRootObject(archive) as NSDataoù archive était un tableau d'une classe personnalisée. J'ai trouvé que déclarer cette classe personnalisée en tant que sous-classe de NSObject et NSCoding a fait l'affaire. Il faudra quelques lignes de plus pour se conformer au protocole de NSCoding, donc ça ressemblera à quelque chose comme ça pour commencer:

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

  func encodeWithCoder(_aCoder: NSCoder) {   }
}
Jeremy Rea
la source