Object X of class Y does not implement methodSignatureForSelector in Swift
Asked Answered
R

3

91

I have a class Person which is instantiated multiple times.Each person get's their own timer. Upon in my init for Person I call 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)")
 }
...
...
}

So I may have 3 instances of Person in an array of Person[]. I am getting an error:

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

I read elsewhere that I should inherit from NSObject but this is in Swift not Obj-C. The function is within the class so I am not sure what to do.

Redshank answered 25/6, 2014 at 18:11 Comment(1)
You already figured out that the class should inherit from NSObject: class Person : NSObject { ... }. Are you looking for a different solution?Merimerida
B
160

Don't think of NSObject as an Objective-C class, think of it as a Cocoa/Foundation class. Even though you're using Swift instead of Objective-C, you're still using all the same frameworks.

Two options: (1) add the dynamic attribute to the function you want to reference as a selector:

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

Or (2) declare Person as a subclass of NSObject, then just call super.init() at the beginning of your initializer:

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()
    }
}
Boathouse answered 25/6, 2014 at 19:9 Comment(5)
You should also be able to decorate the function declaration like this @objc func timerTick(). The NSTimer API seems to be pretty dependent on the Obj-C Runtime.Batt
Good call - added to the answerBoathouse
Thanks this FIXED my issue. But can you explain why? What does it need the @objc part?Krawczyk
NSTimer uses message forwarding to call the targeted selector, which is an Objective-C feature not handled on Swift types by default. When you use the @objc attribute or inherit from an Objective-C class, you're opting into several features, include message forwarding.Boathouse
Neither of these solutions is needed any longer. It is sufficient to declare the selector function dynamic. They are both good and they both still work, but using dynamic on this one function may be seen a more lightweight approach.Paternalism
D
32

Since XCode6 beta 6, you can use 'dynamic' func

dynamic func timerTick() { .... }
Deterge answered 18/9, 2014 at 1:32 Comment(2)
this solved my problem trying to use UILocalizedIndexedCollation.currentCollation()Defence
This is a better approach vs making the entire class inherit from NSObject.Interpleader
B
8

I had a similar error trying to use let encodedArchive = NSKeyedArchiver.archivedDataWithRootObject(archive) as NSData where archive was an array of a custom class. I found that declaring that custom class as a subclass of NSObject and NSCoding did the trick. It'll require a few more lines to conform to the protocol of NSCoding so it'll look something like this to start with:

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

  func encodeWithCoder(_aCoder: NSCoder) {   }
}
Berstine answered 25/8, 2015 at 5:37 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.