Объект X класса Y не реализует methodSignatureForSelector в Swift

89

У меня есть класс Person, который создается несколько раз, и у каждого человека есть свой таймер. После по моему initдля Personменя позвонить 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)")
 }
...
...
}

Так что у меня может быть 3 экземпляра Person в массиве Person[]. Я получаю сообщение об ошибке:

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

Я читал в другом месте, что я должен наследовать, NSObjectно это в Swift, а не в Obj-C. Функция находится внутри класса, поэтому я не уверен, что делать.

Джонстон
источник
4
Вы уже поняли, что класс должен наследовать от NSObject: class Person : NSObject { ... }. Вы ищете другое решение?
Martin R

Ответы:

160

Не думайте о NSObjectклассе Objective-C, думайте о нем как о классе Cocoa / Foundation. Несмотря на то, что вы используете Swift вместо Objective-C, вы все равно используете все те же фреймворки.

Два варианта: (1) добавить dynamicатрибут к функции, на которую вы хотите ссылаться в качестве селектора:

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

Или (2) объявить Personкак подкласс NSObject, а затем просто вызвать super.init()в начале вашего инициализатора:

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
Вы также должны уметь украсить объявление функции таким образом @objc func timerTick(). API NSTimer, похоже, в значительной степени зависит от среды выполнения Obj-C.
macshome
Хороший звонок - добавлено к ответу
Нейт Кук
1
Спасибо, это ИСПРАВЛЕНО мою проблему. Но вы можете объяснить почему? Зачем ему часть @objc?
Aggressor
NSTimerиспользует пересылку сообщений для вызова целевого селектора, который является функцией Objective-C, которая по умолчанию не обрабатывается в типах Swift. Когда вы используете @objcатрибут или наследуете от класса Objective-C, вы выбираете несколько функций, включая пересылку сообщений.
Нейт Кук,
2
Ни одно из этих решений больше не требуется. Достаточно объявить функцию-селектор dynamic. Они оба хороши, и оба по-прежнему работают, но использование dynamicэтой функции можно рассматривать как более легкий подход.
Мэтт
32

Начиная с XCode6 beta 6, вы можете использовать динамическую функцию

dynamic func timerTick() { .... }
Критсана У.
источник
это решило мою проблему, пытаясь использовать UILocalizedIndexedCollation.currentCollation ()
DogCoffee
Это лучший подход, чем наследование всего класса от NSObject.
bobics
8

У меня была аналогичная ошибка при попытке использовать let encodedArchive = NSKeyedArchiver.archivedDataWithRootObject(archive) as NSDataархив, представляющий собой массив настраиваемого класса. Я обнаружил, что объявление этого настраиваемого класса как подкласса NSObject и NSCoding сделало свое дело. Для соответствия протоколу NSCoding потребуется еще несколько строк, поэтому для начала это будет выглядеть примерно так:

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

  func encodeWithCoder(_aCoder: NSCoder) {   }
}
Джереми Ри
источник