When to use a colon with a @selector
As mentioned by boltClock, the character you are referring to is actually a colon. The difference between @selector(method)
and @selector(method:)
is the method signature. The 2nd variant expects a parameter to be passed.
@selector(method)
would expect the method: -(void)method
@selector(method:)
would expect the method: -(void)method:(id)someParameter
Use colon : or not with selectors
The colon is needed after the method's name if and only if the method takes an argument.
No function parameters:
-(void)addAction {}
// Use ...@selector(addAction)...
Has parameter:
-(void)addAction:(id)info {}
// Use ...@selector(addAction:)...
Objective-C performSelector when to use colon?
The colon represents a method argument. Since myMethod takes no arguments its selector can't have a colon. If you had multiple arguments like this...
- (void)myMethod:(id)method object:(id)object enabled:(BOOL)bool {
// Some Stuff
}
... the selector would be @selector(myMethod:object:enabled:)
Objective C method naming convention - usage of colon
The wrong way is legal, yes, but bad style. Just don't do it. The colons are part of what make ObjC code nice to read (though verbose); all the arguments at the call site have labels that remind you what they are for and often what type they should be.
The space is not in fact part of the method name; it's ignored. This is one of those things about ObjC that's pretty much "compiler-specified".
@interface Jackson : NSObject
- (void)shootCannon :(double)range;
- (void)grillSandwichWithBread :(BreadType)bread cheese :(CheeseType)cheese;
@end
@implementation Jackson
@end
int main(int argc, const char * argv[])
{
@autoreleasepool {
NSLog(@"%@", NSStringFromSelector(@selector(shootCannon:)));
NSLog(@"%@", NSStringFromSelector(@selector(shootCannon :)));
NSLog(@"%@", NSStringFromSelector(@selector(grillSandwichWithBread:cheese:)));
NSLog(@"%@", NSStringFromSelector(@selector(grillSandwichWithBread :cheese :)));
NSLog(@"%d", @selector(shootCannon:) == @selector(shootCannon :));
NSLog(@"%d", @selector(grillSandwichWithBread:cheese:) == @selector(grillSandwichWithBread :cheese :));
}
return 0;
}
2014-04-05 23:59:29.548 MethodNameSpace[66948:303] shootCannon:
2014-04-05 23:59:29.548 MethodNameSpace[66948:303] shootCannon:
2014-04-05 23:59:29.549 MethodNameSpace[66948:303] grillSandwichWithBread:cheese:
2014-04-05 23:59:29.550 MethodNameSpace[66948:303] grillSandwichWithBread:cheese:
2014-04-05 23:59:29.550 MethodNameSpace[66948:303] 1
2014-04-05 23:59:29.551 MethodNameSpace[66948:303] 1
Selectors in Objective-C?
You have to be very careful about the method names. In this case, the method name is just "lowercaseString
", not "lowercaseString:
" (note the absence of the colon). That's why you're getting NO
returned, because NSString
objects respond to the lowercaseString
message but not the lowercaseString:
message.
How do you know when to add a colon? You add a colon to the message name if you would add a colon when calling it, which happens if it takes one argument. If it takes zero arguments (as is the case with lowercaseString
), then there is no colon. If it takes more than one argument, you have to add the extra argument names along with their colons, as in compare:options:range:locale:
.
You can also look at the documentation and note the presence or absence of a trailing colon.
xcode syntax question regarding @selector
IBAction
is typedef'd to void
. Its only function is to allow Interface Builder to map your interface elements to actions if your interface is in a nib file. It's declared in your interface file only so Interface Builder can see it. Thus there's no real difference between - (IBAction)myAction
and - (void)myAction
.
If your action method doesn't need to know what control/view triggered the action (i.e. it always does the same thing when the action is invoked on the target), you can omit the (id)sender
parameter and pass in a selector without the colon.
If you're not using Interface Builder you don't have to use the IBAction
format, and if your methods will only be invoked within the same class then there's no need to expose them in your header file (nor is there any harm in doing so).
2 quickies about NSNotification Center
First, most all examples assume that you are passing the notification to the method. For example:
- (void)respondToNotification:(NSNotification *);
This would be represented as:
@selector(respondToNotification:)
If you removed this argument, it would be
@selector(respondToNotification)
As for the second item, you cannot specify arguments in that manner. You need to architect your methods in such a way that it receives the notification as the argument, and then you can inspect the notification and call another method to perform whatever action you need.
@selector() in Swift?
Swift itself doesn't use selectors — several design patterns that in Objective-C make use of selectors work differently in Swift. (For example, use optional chaining on protocol types or is
/as
tests instead of respondsToSelector:
, and use closures wherever you can instead of performSelector:
for better type/memory safety.)
But there are still a number of important ObjC-based APIs that use selectors, including timers and the target/action pattern. Swift provides the Selector
type for working with these. (Swift automatically uses this in place of ObjC's SEL
type.)
In Swift 2.2 (Xcode 7.3) and later (including Swift 3 / Xcode 8 and Swift 4 / Xcode 9):
You can construct a Selector
from a Swift function type using the #selector
expression.
let timer = Timer(timeInterval: 1, target: object,
selector: #selector(MyClass.test),
userInfo: nil, repeats: false)
button.addTarget(object, action: #selector(MyClass.buttonTapped),
for: .touchUpInside)
view.perform(#selector(UIView.insertSubview(_:aboveSubview:)),
with: button, with: otherButton)
The great thing about this approach? A function reference is checked by the Swift compiler, so you can use the #selector
expression only with class/method pairs that actually exist and are eligible for use as selectors (see "Selector availability" below). You're also free to make your function reference only as specific as you need, as per the Swift 2.2+ rules for function-type naming.
(This is actually an improvement over ObjC's @selector()
directive, because the compiler's -Wundeclared-selector
check verifies only that the named selector exists. The Swift function reference you pass to #selector
checks existence, membership in a class, and type signature.)
There are a couple of extra caveats for the function references you pass to the #selector
expression:
- Multiple functions with the same base name can be differentiated by their parameter labels using the aforementioned syntax for function references (e.g.
insertSubview(_:at:)
vsinsertSubview(_:aboveSubview:)
). But if a function has no parameters, the only way to disambiguate it is to use anas
cast with the function's type signature (e.g.foo as () -> ()
vsfoo(_:)
). - There's a special syntax for property getter/setter pairs in Swift 3.0+. For example, given a
var foo: Int
, you can use#selector(getter: MyClass.foo)
or#selector(setter: MyClass.foo)
.
General notes:
Cases where #selector
doesn't work, and naming: Sometimes you don't have a function reference to make a selector with (for example, with methods dynamically registered in the ObjC runtime). In that case, you can construct a Selector
from a string: e.g. Selector("dynamicMethod:")
— though you lose the compiler's validity checking. When you do that, you need to follow ObjC naming rules, including colons (:
) for each parameter.
Selector availability: The method referenced by the selector must be exposed to the ObjC runtime. In Swift 4, every method exposed to ObjC must have its declaration prefaced with the @objc
attribute. (In previous versions you got that attribute for free in some cases, but now you have to explicitly declare it.)
Remember that private
symbols aren't exposed to the runtime, too — your method needs to have at least internal
visibility.
Key paths: These are related to but not quite the same as selectors. There's a special syntax for these in Swift 3, too: e.g. chris.valueForKeyPath(#keyPath(Person.friends.firstName))
. See SE-0062 for details. And even more KeyPath
stuff in Swift 4, so make sure you're using the right KeyPath-based API instead of selectors if appropriate.
You can read more about selectors under Interacting with Objective-C APIs in Using Swift with Cocoa and Objective-C.
Note: Before Swift 2.2, Selector
conformed to StringLiteralConvertible
, so you might find old code where bare strings are passed to APIs that take selectors. You'll want to run "Convert to Current Swift Syntax" in Xcode to get those using #selector
.
Objective-C: How to perform a performSelector:@selector?
You've already created selector from string - pass it to performSelector: method:
[self performSelector:customSelector withObject:0];
Edit: Mind, that if your method takes parameter then you must use colon when create selector from it:
// Note that you may need colon here:
[NSString stringWithFormat:@"abcWith%@:", table]
Objective-C: Calling selectors with multiple arguments
Your method signature is:
- (void) myTest:(NSString *)
withAString happens to be the parameter (the name is misleading, it looks like it is part of the selector's signature).
If you call the function in this manner:
[self performSelector:@selector(myTest:) withObject:myString];
It will work.
But, as the other posters have suggested, you may want to rename the method:
- (void)myTestWithAString:(NSString*)aString;
And call:
[self performSelector:@selector(myTestWithAString:) withObject:myString];
Related Topics
Xcode 4.3.2, Issue with Running on Simulator
Round Top Corners of a Uiview and Add Border
Disabling Firebase Automatic Screen Reporting
What Is the Swift Equivalent of Makeobjectsperformselector
-[Not a Type Retain]: Message Sent to Deallocated Instance
iOS Swift: Could Not Cast Value Type '_Nscfnumber' to 'Nsstring'
Swiftui Show/Hide Title Issues with Navigationbar
Weak or Strong for Iboutlet and Other
Changing Uitableview's Section Header/Footer Title Without Reloading the Whole Table View
Locationmanager:Didenterregion Not Called When a Beacon Is Detected
Why Is Allocating or Initializing Nsdateformatter Considered "Expensive"
How to Determine Which Textfield Is Active Swift
How to Get a Swift Type Name as a String with Its Namespace (Or Framework Name)
Swift Compatibility Between Versions for a Library
iOS Web Page Errors Over Cellular Data But Not Over Wifi? Recent Change to At&T Cellular Network