When to Use a Colon with a @Selector

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:) vs insertSubview(_:aboveSubview:)). But if a function has no parameters, the only way to disambiguate it is to use an as cast with the function's type signature (e.g. foo as () -> () vs foo(_:)).
  • 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



Leave a reply



Submit