The “Unrecognized selector sent to instance” is an annoying error in Xcode. With Swift’s syntax improvements this error occurs less often, but it still pops up every now and then. How do you solve it?
In this tutorial, you’ll learn to debug Unrecognized selector sent to instance. We’ll discuss where the error comes from and how to solve it.
Debugging is part of becoming an effective iOS developer. Fixing errors is a great way to learn more about iOS development, so take your time to go in-depth.
Ready? Let’s go.
Selectors are a relic from the past, from Objective-C. With the latest Swift, you shouldn’t run into that Unrecognized selector sent to instance problem that often.
But… what’s a selector?
A selector is essentially the name of a function. You “select” which function to execute on an object based on the selector. You can safely assume that a selector is identical to the name of a function.
Selectors come from Objective-C:
SEL
or Selector
is the variable type of a selector@selector()
function or with NSSelectorFromString(@"methodName")
performSelector:
function, and test whether an selector exists with respondsToSelector:
#selector(···)
to indicate a function to be called, with the target-action mechanismSo why does this Unrecognized selector sent to instance error then show up in Swift code?
A large part of the Cocoa Touch SDK, i.e. the framework code you use to create native iOS apps, is still written in Objective-C! Especially the UIKit
framework, the one that puts views, images and buttons on your iPhone screen, is written in Objective-C.
Many Objective-C classes use the target-action mechanism. It’s a special way of calling functions. Check this out:
let gesture = UITapGestureRecognizer(target: self, action: #selector(onTapGesture(_:)))
gesture.numberOfTapsRequired = 1
view.addGestureRecognizer(gesture)
@objc func onTapGesture(_ gesture:UITapGestureRecognizer)
{
print("Tapped!!")
}
In the above example, you’re creating a gesture recognizer that executes the onTapGesture(_:)
function when you tap the screen once. This code can be part of a UIViewController
, for instance.
Do you see the target
and action
parameters in the UITapGestureRecognizer
initializer function?
With the target-action design pattern you’re essentially saying: “When this happens, execute that function.”
The target
is the object the function is called upon, and the action
is the function that should be called. In many cases you can also designate an event with the for
parameter, for instance to execute the action when the value on a textfield changes.
Now you know where selectors come from!
Selectors have been part of the Swift programming language since it was invented, because selectors originally come from Objective-C. Because the iOS SDK is partly written in Objective-C, you still work with selectors in Swift.
Before Swift 2.2 you used Objective-C selectors like this:
let gesture = UITapGestureRecognizer(target: self, action: Selector("onTapGesture:"))
See how the Selector
initializer takes one argument, a string, that contains the name of the function that should be called? Now you’re starting to see where this might go wrong…
The string with the selector can’t be checked at runtime! The Swift compiler can’t check whether the function actually exists before you run your app, so if you’ve written the selector incorrectly, you’ll get that Unrecognized selector sent to instance.
If Swift could have checked whether the selector exists when compiling the app, it would have shown you the Use of unresolved identifier error.
On top of that, the syntax for writing out selectors is confusing. Ever noticed those colons, parentheses and underscores?
onTapGesture()
onTapGesture(_:)
onTapGesture(_:_:)
, i.e. one _:
for every unnamed parameteronTapGesture(gesture:)
onTapGesture(gesture:view:event:)
Objective-C doesn’t have parentheses for function names, because of its bracket-style messaging syntax, so you wouldn’t write them in the selector string: onTapGesture:
.
Quick Tip: If you want to know the selector or function name of the current function, use print(#function)
in Swift.
So… here’s the kicker: since Swift version 2.2, selectors can be checked at during compilation thanks to a new syntax:
let gesture = UITapGestureRecognizer(target: self, action: #selector(onTapGesture(_:)))
Instead of using a string, the new #selector()
syntax refers directly to the function that should be called when the event takes place.
Because of this, Swift can pre-check during compilation whether a function exists, and inform you of an error before you run your app. The result? Less time debugging your app!
In Swift 4 and later you’ll have to designate functions that you want to use as selectors for target/action-based interactions with . This is because Swift 4 doesn’t automatically make Swift functions available to Objective-C.
The quirky way of writing functions with underscores and colons remains, however. Fortunately, you can use code autocompletion to get hints, like this:
Simply place your cursor between the parentheses of #selector()
and press the Esc
-key. A dropdown menu shows up, and you can scroll to the right function.
Still got the error? Let’s get to fixing it…
Alright, enough with the explaining! You now know where the Unrecognized selector sent to instance comes from and what causes it – so let’s get to fixing.
If you’re coding Objective-C, or working with Swift 1.0, 2.0 or 2.1. (i.e. before Swift 2.2), this solution is for you.
With Swift 2.2, the error shouldn’t happen, because it’s checked at compile-time. If you still run into issues with a selector, check this tutorial and use code completion to get to the right selector.
First off, let’s be real:
If you’re debugging an Objective-C library, or you can’t upgrade to the latest Swift version, do this:
target
argument for the target-action design pattern. In many cases, the target
should be self
.print(#function)
(Swift >= 2.2), or print(__FUNCTION__)
(Swift < 2.2), or NSLog(@"%s", __PRETTY_FUNCTION__)
in Objective-C to find out the correct selector.In some cases, Xcode won’t take you to the offending line after a crash. When your app crashes you won’t see the line of code that caused the crash, but instead you’ll be taken to your AppDelegate
class. How do you find the incorrect line of code now?
If you look at the entire error output, you’ll see that it has caused an exception:
*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[MyApp.MyViewController viewDidLoad:]: unrecognized selector sent to instance 0xDEADBEEF'
You can catch that exception with a clever trick:
+
-button in the bottom-right cornerRun your app and trigger the error. You should now see the line that is incorrect, because thanks to the breakpoint your code “breaks” on the line of code that threw the exception.
In rare cases, especially if you haven’t managed memory correctly, you can get the Unrecognized selector sent to instance error on an object that shouldn’t have received that selector in the first place.
This can happen when you incorrectly retain and/or release objects, causing an object to be allocated on a memory address that was previously associated with another object. Because this new object doesn’t have the selector you’re trying to call, you’ll see the error. You can solve this by tracing the memory leak.
Alright! Solved the error? Awesome! Now you know what selectors are, how to use them, and why they can cause all sorts of problems…
Want to learn more? Check out these resources: