Obiekt X klasy Y nie implementuje metody methodSignatureForSelector w języku Swift

89

Mam klasę Person, której instancja jest uruchamiana wiele razy. Każda osoba ma swój własny licznik czasu. Po w moim initdla Personzadzwonię 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)")
 }
...
...
}

Więc mogę mieć 3 wystąpienia Person w tablicy Person[]. Otrzymuję błąd:

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

Czytałem gdzie indziej, po czym powinienem dziedziczyć, NSObjectale to jest w języku Swift, a nie Obj-C. Funkcja znajduje się w klasie, więc nie wiem, co robić.

Johnston
źródło
4
Już zorientowali się, że klasa powinna dziedziczyć NSObject: class Person : NSObject { ... }. Szukasz innego rozwiązania?
Martin R

Odpowiedzi:

160

Nie myśl o NSObjectklasie Objective-C, myśl o niej jak o klasie Cocoa / Foundation. Mimo że używasz Swift zamiast Objective-C, nadal używasz tych samych frameworków.

Dwie opcje: (1) dodaj dynamicatrybut do funkcji, do której chcesz się odwołać jako selektor:

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

Lub (2) zadeklaruj Personjako podklasę NSObject, a następnie wywołaj super.init()na początku swojego inicjatora:

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
źródło
3
Powinieneś także być w stanie ozdobić deklarację funkcji w ten sposób @objc func timerTick(). Wydaje się, że API NSTimer jest dość zależne od środowiska wykonawczego Obj-C.
macshome
Dobre połączenie - dodane do odpowiedzi
Nate Cook
1
Dzięki temu NAPRAWIONO mój problem. Ale czy możesz wyjaśnić, dlaczego? Czego potrzebuje część @objc?
Aggressor
NSTimerużywa przekazywania wiadomości do wywołania selektora docelowego, co jest funkcją celu-C, która nie jest obsługiwana domyślnie w typach Swift. Używając @objcatrybutu lub dziedzicząc z klasy Objective-C, decydujesz się na kilka funkcji, w tym przekazywanie wiadomości.
Nate Cook
2
Żadne z tych rozwiązań nie jest już potrzebne. Wystarczy zadeklarować funkcję selektora dynamic. Obie są dobre i nadal działają, ale użycie dynamictej jednej funkcji może być postrzegane jako bardziej lekkie podejście.
Matt
32

Od wersji beta 6 XCode6 możesz używać funkcji „dynamicznej”

dynamic func timerTick() { .... }
Kritsana U.
źródło
rozwiązało to mój problem przy próbie użycia UILocalizedIndexedCollation.currentCollation ()
DogCoffee
Jest to lepsze podejście niż sprawianie, że cała klasa dziedziczy po NSObject.
bobics
8

Wystąpił podobny błąd podczas próby użycia, let encodedArchive = NSKeyedArchiver.archivedDataWithRootObject(archive) as NSDatagdzie archiwum było tablicą klasy niestandardowej. Okazało się, że zadeklarowanie tej klasy niestandardowej jako podklasy NSObject i NSCoding załatwiło sprawę. Będzie to wymagało jeszcze kilku wierszy, aby dostosować się do protokołu NSCoding, więc na początku będzie wyglądać mniej więcej tak:

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

  func encodeWithCoder(_aCoder: NSCoder) {   }
}
Jeremy Rea
źródło