Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to correctly pass selector as parameter in swift

Tags:

Conclusively speaking

I have class A that contains a instances of B. And in class A, I pass a function of A as selector to A method of B. And B use this selector to register notification. However, when the notification comes in, it could not run the selector and show "unrecognized selector sent to instance". If I move all what I want to do in class B into class A, it worked. However, I want them separated so that it seems more organized. I am fairly new to Objective-C and Swift, therefore, I don't know how to pass selector as parameter in this case. Answer in Swift would be great.

ViewController.swift

class ViewController: UIViewController {      var sessionCtrl : GKSessionControllerH!      override func viewDidLoad() {         super.viewDidLoad()         // Do any additional setup after loading the view, typically from a nib.          sessionCtrl = GKSessionControllerH()          //  register notifications         registerNotification()     }      func registerNotification() {         sessionCtrl.registerNotification(GKGesture.Up, gestureHandler: "gestureUpHandler")     }      func gestureUpHandler() {         dispatch_async(dispatch_get_main_queue()) {             self.slidesViewCtrl!.prevPage()         }     } } 

GKSessionControllerH.swift

class GKSessionControllerH: NSObject, WCSessionDelegate {      func handleGestureContent(content : AnyObject?) {          // retrieve gesture         let gesture = GKGesture(rawValue: content as! String)!         print("Handheld device receives \(gesture)")          //  post notification         let notificationName = "ReceiveGesture\(gesture.rawValue)"         NSNotificationCenter.defaultCenter().postNotificationName(notificationName, object: nil)      }      func registerNotification(gesture : GKGesture, gestureHandler : Selector) {          let notificationName = "ReceiveGesture\(gesture.rawValue)"         NSNotificationCenter.defaultCenter().addObserver(self, selector: gestureHandler, name: notificationName, object: nil)      } } 

debug info

2015-07-08 17:26:26.534 Slider[4608:1719498] -[Slider.GKSessionControllerH gestureDownHandler]: unrecognized selector sent to instance 0x7f912857a420 2015-07-08 17:26:26.543 Slider[4608:1719498] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[Slider.GKSessionControllerH gestureDownHandler]: unrecognized selector sent to instance 0x7f912857a420' *** First throw call stack: (     0   CoreFoundation                      0x000000010430dca5 __exceptionPreprocess + 165     1   libobjc.A.dylib                     0x00000001060f1dcd objc_exception_throw + 48     2   CoreFoundation                      0x0000000104315fcd -[NSObject(NSObject) doesNotRecognizeSelector:] + 205     3   CoreFoundation                      0x00000001042634ea ___forwarding___ + 970     4   CoreFoundation                      0x0000000104263098 _CF_forwarding_prep_0 + 120     5   CoreFoundation                      0x00000001042db09c __CFNOTIFICATIONCENTER_IS_CALLING_OUT_TO_AN_OBSERVER__ + 12     6   CoreFoundation                      0x00000001042daddb _CFXRegistrationPost + 427     7   CoreFoundation                      0x00000001042dab42 ___CFXNotificationPost_block_invoke + 50     8   CoreFoundation                      0x000000010431d432 -[_CFXNotificationRegistrar find:object:observer:enumerator:] + 1618     9   CoreFoundation                      0x00000001041d3538 _CFXNotificationPost + 632     10  Foundation                          0x00000001048bb3c4 -[NSNotificationCenter postNotificationName:object:userInfo:] + 66     11  Slider                              0x00000001040f4e0e _TFC6Slider20GKSessionControllerH20handleGestureContentfS0_FGSqPSs9AnyObject__T_ + 1086     12  Slider                              0x00000001040f4689 _TFC6Slider20GKSessionControllerH7sessionfS0_FTCSo9WCSession17didReceiveMessageGVSs10DictionarySSPSs9AnyObject___T_ + 825     13  Slider                              0x00000001040f49b7 _TToFC6Slider20GKSessionControllerH7sessionfS0_FTCSo9WCSession17didReceiveMessageGVSs10DictionarySSPSs9AnyObject___T_ + 119     14  WatchConnectivity                   0x00000001060c18cd WatchConnectivity + 35021     15  libdispatch.dylib                   0x0000000106ab2b11 _dispatch_call_block_and_release + 12     16  libdispatch.dylib                   0x0000000106ad280d _dispatch_client_callout + 8     17  libdispatch.dylib                   0x0000000106ab92ec _dispatch_queue_drain + 2200     18  libdispatch.dylib                   0x0000000106ab88ed _dispatch_queue_invoke + 233     19  libdispatch.dylib                   0x0000000106abae9b _dispatch_root_queue_drain + 1412     20  libdispatch.dylib                   0x0000000106aba912 _dispatch_worker_thread3 + 111     21  libsystem_pthread.dylib             0x0000000106e11637 _pthread_wqthread + 729     22  libsystem_pthread.dylib             0x0000000106e0f40d start_wqthread + 13 ) libc++abi.dylib: terminating with uncaught exception of type NSException 
like image 547
Jieyi Hu Avatar asked Jul 08 '15 22:07

Jieyi Hu


1 Answers

Here's the big clue in your console output:

-[Slider.GKSessionControllerH gestureDownHandler]: unrecognized selector sent to instance 0x7f912857a420

So, the problem is that rather than attempting to call gestureDownHandler on your ViewController, GKSessionControllerH is registering itself to be the receiver of the notification.

We need to pass in both the selector and the object to call the selector on.

func registerNotification(gesture: GKGesture, gestureHandler: AnyObject, selector: Selector) {     let notificationName = "ReceiveGesture\(gesture.rawValue)"     NSNotificationCenter.defaultCenter().addObserver(gestureHandler, selector: gestureHandler, name: notificationName, object: nil) } 

And now, to register:

sessionCtrl.registerNotification(.Up, gestureHandler: self, selector: "gestureUpHandler") 

Alternatively, and arguably more Swift-like, we can take a more closure-based approach.

First, let's make GKSessionControllerH receive the notifications, and we'll pass it a closure, which it'll keep track of to call when the notification is received.

In GKSessionControllerH,

var gestureActions = [()->Void] // an array of void-void closures  func gestureHandler() {     for action in gestureActions {         action()     } }  func registerNotification(gesture: GKGesture, action:()->Void) {     let notificationName = "ReceiveGesture\(gesture.rawValue)"     NSNotificationCenter.defaultCenter().addObserver(self, selector: "gestureHandler", name: notificationName, object: nil) } 

And now, we pass in a closure (which can be a method):

In ViewController:

func registerNotification() {     sessionCtrl.registerNotification(.Up, action: gestureUpHandler) } 

Now obviously, this will need a little more logic to handle all your different gesture types, but the gist of it is here.

like image 55
nhgrif Avatar answered Oct 20 '22 04:10

nhgrif