简体   繁体   中英

Swift selector to protocol function?

I have code like this:

protocol FooP {
   ...
}

extension FooP {
   func doFoo() {
      print("foo")
   }
   func doFoo(timer: NSTimer) {
      doFoo()
   }
}
class A : NSObject, UITableViewDataSource, FooP {
   var timer : NSTimer?

   ...

   func startUpdating() {
      timer = NSTimer.scheduledTimerWithTimeInterval(
         1.0,
         target: self,
         selector: Selector("doFoo:"),
         userInfo: nil, 
         repeats: true
      )
   }
}

Unfortunately it crashes when I start timer the program crashes with

Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[xyz.A doFoo:]: unrecognized selector sent to instance 0x7fb2041c4ac0'

How i can make it work (I want to keep implementation of doFoo inside protocol)?

If I move doFoo into A class definition everything works fine, but as i said i want to implement this function inside protocol.

In other words I need selector that says

"Hey I point to function named "doFoo" that is implemented as extension to FooP"

Right now selector seems to say

"Hey I point to function named "doFoo" that is implemented in A class"

Try to play in your playground. Your trouble is, that there is no possibility to define @objc func in protocol extension. So, see possible workaround

import XCPlayground
XCPlaygroundPage.currentPage.needsIndefiniteExecution = true
import Foundation

protocol FooP {
}

extension FooP {
    func doFoo() {
        print("foo")
    }
    func doFoo(timer: NSTimer) {
        print("dofoo")
        doFoo()
    }
}
class A: FooP {
    var timer : NSTimer?
    @objc func foo(timer: NSTimer) {
        doFoo(timer)
    }
    func startUpdating() {
        timer = NSTimer.scheduledTimerWithTimeInterval(
            1.0,
            target: self,
            selector: "foo:",
            userInfo: nil,
            repeats: true
        )
    }
}

let a = A()
a.startUpdating()

Why it works for you if you move doFoo inside class A? That is because your class inherits from NSObject, so @objc keyword is not necessary.

The problem is, NSTimer and the whole Selector() business are Objective-C stuff and do work in Swift domain thanks to bridging. However, Swift's default protocol implementations are not bridged to Objective-C wonderland (yet), and that's why your timer fails. Basically, from Objective-C perspective objects of type A do not respond to a selector doFoo: , period.

So, report this use-case to swift-evolution for the long-term solution. Short-term, use some sort of a workaround.

BTW, you might find it interesting to read (or even participate) in this thread .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM