O objeto X da classe Y não implementa o métodoSignatureForSelector em Swift

89

Tenho uma classe Person que é instanciada várias vezes. Cada pessoa tem seu próprio cronômetro. Em cima do meu initpara Personeu chamo 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)")
 }
...
...
}

Portanto, posso ter 3 instâncias de Person em uma matriz de Person[]. Estou recebendo um erro:

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

Li em outro lugar que deveria herdar, NSObjectmas isso está em Swift, não em Obj-C. A função é dentro da classe, então não tenho certeza do que fazer.

Johnston
fonte
4
Você já descobriu que a classe deve herdar de NSObject: class Person : NSObject { ... }. Você está procurando uma solução diferente?
Martin R

Respostas:

160

Não pense NSObjectem uma classe Objective-C, pense nela como uma classe Cocoa / Foundation. Mesmo que você esteja usando Swift em vez de Objective-C, você ainda está usando as mesmas estruturas.

Duas opções: (1) adicionar o dynamicatributo à função que você deseja referenciar como um seletor:

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

Ou (2) declare Personcomo uma subclasse de e NSObject, em seguida, chame super.init()no início do seu 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
fonte
3
Você também deve ser capaz de decorar a declaração da função assim @objc func timerTick(). A API NSTimer parece ser bastante dependente do Obj-C Runtime.
macshome
Boa chamada - adicionada à resposta
Nate Cook
1
Obrigado por este CORRIGIDO meu problema. Mas você pode explicar por quê? O que precisa da parte @objc?
Agressor de
NSTimerusa o encaminhamento de mensagens para chamar o seletor de destino, que é um recurso Objective-C não tratado em tipos Swift por padrão. Quando você usa o @objcatributo ou herda de uma classe Objective-C, está optando por vários recursos, incluindo o encaminhamento de mensagens.
Nate Cook
2
Nenhuma dessas soluções é mais necessária. Basta declarar a função do seletor dynamic. Ambos são bons e ainda funcionam, mas o uso dynamicdesta função pode ser visto como uma abordagem mais leve.
matt
32

Desde XCode6 beta 6, você pode usar funções 'dinâmicas'

dynamic func timerTick() { .... }
Kritsana U.
fonte
isso resolveu meu problema ao tentar usar UILocalizedIndexedCollation.currentCollation ()
DogCoffee
Esta é uma abordagem melhor do que fazer com que toda a classe herde de NSObject.
bobics
8

Eu tive um erro semelhante ao tentar usar let encodedArchive = NSKeyedArchiver.archivedDataWithRootObject(archive) as NSDataonde archive era uma matriz de uma classe personalizada. Descobri que declarar essa classe personalizada como uma subclasse de NSObject e NSCoding resolveu o problema. Isso exigirá mais algumas linhas para estar em conformidade com o protocolo de NSCoding, então, para começar, terá a seguinte aparência:

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

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