Using -performSelector: vs. just calling the method
Asked Answered
H

5

117

I'm still kind of new to Objective-C and I'm wondering what is the difference between the following two statements?

[object performSelector:@selector(doSomething)]; 

[object doSomething];
Hypesthesia answered 29/9, 2009 at 15:1 Comment(0)
O
194

Basically performSelector allows you to dynamically determine which selector to call a selector on the given object. In other words the selector need not be determined before runtime.

Thus even though these are equivalent:

[anObject aMethod]; 
[anObject performSelector:@selector(aMethod)]; 

The second form allows you to do this:

SEL aSelector = findTheAppropriateSelectorForTheCurrentSituation();
[anObject performSelector: aSelector];

before you send the message.

Obit answered 29/9, 2009 at 15:9 Comment(7)
It's worth pointing out that you would actually assign the result of findTheAppropriateSelectorForTheCurrentSituation() to aSelector, then invoke [anObject performSelector:aSelector]. @selector produces a SEL.Isabel
Using performSelector: is something you probably only do if you implement target-action in your class. The siblings performSelectorInBackground:withObject: and performSelectorOnMainThread:withObject:waitUntilDone: are often more useful. For spawning a background thread, and for calling back results to the main thread from said background thread.Pedanticism
performSelector is also useful to suppress compile warnings. If you know the method exists (like after using respondsToSelector), it will stop Xcode from saying "may not respond to your_selector". Just don't use it instead of finding out the real cause of the warning. ;)Cantabile
I read on some other thread on StackOverflow that use of performSelector was a reflection of a horrible design, and it had tons of thumbs up. I wish I could find it again. I searched google, restricting results to stackoverflow, and got 18,000 results. Eww.Eyeball
"reflection of a horrible design" is overly simplistic. This was what we had before blocks were available, and not all uses are bad, then or now. Although now that blocks are available, that's probably a better choice for new code unless you are doing something very simple.Capitalize
performSelector lets you glimpse under the hood, and understand how ObjC messages are handled. The selector is actually a null terminated C string, which serves as a key string obj-C assigns to some method implementation (which is actually an IMPL - a C function). I've been using performSelector in many places, and it is GOOD.Cybele
One nice use of performSelector, is for sending a message without #import the header of the receiver. This way, you can decouple sources, and make some publicly available, without carrying #imports of other classes.Cybele
C
17

For this very basic example in the question,

[object doSomething];
[object performSelector:@selector(doSomething)]; 

there is no difference in what is going to happen. doSomething will be synchronously executed by object. Only "doSomething" is a very simple method, that does not return anything, and does not require any parameters.

were it something a little more complicated, like:

(void)doSomethingWithMyAge:(NSUInteger)age;

things would get complicated, because [object doSomethingWithMyAge:42];

can no longer be called with any variant of "performSelector", because all variants with parameters only accept object parameters.

The selector here would be "doSomethingWithMyAge:" but any attempt to

[object performSelector:@selector(doSomethingWithMyAge:) withObject:42];  

simply won't compile. passing an NSNumber: @(42) instead of 42, wouldn't help either, because the method expects a basic C type - not an object.

In addition, there are performSelector variants up to 2 parameters, no more. While methods many times have many more parameters.

I have found out that although synchronous variants of performSelector:

- (id)performSelector:(SEL)aSelector;
- (id)performSelector:(SEL)aSelector withObject:(id)object;
- (id)performSelector:(SEL)aSelector withObject:(id)object1 withObject:(id)object2;

always return an object, I was able to return a simple BOOL or NSUInteger too, and it worked.

One of the two main uses of performSelector is to compose dynamically the name of the method you want to execute, as explained in a previous answer. For example

 SEL method = NSSelectorFromString([NSString stringWithFormat:@"doSomethingWithMy%@:", @"Age");
[object performSelector:method];

The other use, is to asynchronously dispatch a message to object, that will be executed later on the current runloop. For this, there are several other performSelector variants.

- (void)performSelector:(SEL)aSelector withObject:(id)anArgument afterDelay:(NSTimeInterval)delay inModes:(NSArray *)modes;
- (void)performSelector:(SEL)aSelector withObject:(id)anArgument afterDelay:(NSTimeInterval)delay;
- (void)performSelector:(SEL)aSelector target:(id)target argument:(id)arg order:(NSUInteger)order modes:(NSArray *)modes;
- (void)performSelectorOnMainThread:(SEL)aSelector withObject:(id)arg waitUntilDone:(BOOL)wait modes:(NSArray *)array;
- (void)performSelectorOnMainThread:(SEL)aSelector withObject:(id)arg waitUntilDone:(BOOL)wait;
- (void)performSelector:(SEL)aSelector onThread:(NSThread *)thr withObject:(id)arg waitUntilDone:(BOOL)wait modes:(NSArray *)array;
- (void)performSelector:(SEL)aSelector onThread:(NSThread *)thr withObject:(id)arg waitUntilDone:(BOOL)wait;
- (void)performSelectorInBackground:(SEL)aSelector withObject:(id)arg;

(yes I gathered them from several Foundation class categories, like NSThread, NSRunLoop and NSObject)

Each of the variants has its own special behavior, but all share something in common (at least when waitUntilDone is set to NO). The "performSelector" call would return immediately, and the message to object will only be put on the current runloop after some time.

Because of the delayed execution - naturally no return value is available form the method of the selector, hence the -(void) return value in all these asynchronous variants.

I hope I covered this somehow...

Cybele answered 3/8, 2014 at 21:52 Comment(0)
R
12

@ennuikiller is spot on. Basically, dynamically-generated selectors are useful for when you don't (and usually can't possibly) know the name of the method you'll be calling when you compile the code.

One key difference is that -performSelector: and friends (including the multi-threaded and delayed variants) are somewhat limited in that they are designed for use with methods with 0-2 parameters. For example, calling -outlineView:toolTipForCell:rect:tableColumn:item:mouseLocation: with 6 parameters and returning the NSString is pretty unwieldy, and not supported by the provided methods.

Ries answered 29/9, 2009 at 16:1 Comment(2)
To do that, you'd need to use an NSInvocation object.Osbert
Another difference: performSelector: and friends all take object arguments, meaning you can't use them to call (for example) setAlphaValue:, because its argument is a float.Ashram
I
4

Selectors are a bit like function pointers in other languages. You use them when you don't know at compile time which method you want to call at runtime. Also, like function pointers, they only encapsulate the verb part of invocation. If the method has parameters, you will need to pass them as well.

An NSInvocation serves a similar purpose, except that it binds together more information. Not only does it include the verb part, it also includes the target object and the parameters. This is useful when you want to call a method on a particular object with particular parameters, not now but in the future. You can build an appropriate NSInvocation and fire it later.

Isabel answered 29/9, 2009 at 16:58 Comment(3)
Selectors really aren't at all like a function pointer in that a function pointer is something you can call with arguments and a selector can be used to call a particular method on any object that implements it; a selector does not have the full context of invocation like a function pointer.Caramel
Selectors aren't the same as function pointers, but I still think they are similar. They represent verbs. C function pointers also represent verbs. Neither is useful without additional context. Selectors require an object and parameters; function pointers require parameters (which might include an object upon which to operate). My point was to highlight how they are different from NSInvocation objects, which do contain all the necessary context. Perhaps my comparison was confusing, in which case I apologize.Isabel
Selectors aren't function pointers. Not even close. They are simple C strings in reality, that contain a "name" of a method (as opposed to 'function'). They're not even method signatures, because they do not embed the types of parameters. An object can have more than one method for the same selector (different param types, or different return type).Cybele
H
-8

There is another subtle difference between the two.

    [object doSomething]; // is executed right away

    [object performSelector:@selector(doSomething)]; // gets executed at the next runloop

Here is the excerpt from Apple Documentation

"performSelector:withObject:afterDelay: Performs the specified selector on the current thread during the next run loop cycle and after an optional delay period. Because it waits until the next run loop cycle to perform the selector, these methods provide an automatic mini delay from the currently executing code. Multiple queued selectors are performed one after another in the order they were queued."

Herwin answered 5/11, 2011 at 1:29 Comment(2)
Your answer is factually incorrect. The documentation you quote is about performSelector:withObject:afterDelay:, but the question and your snippet are using performSelector:, which is an entirely different method. From the docs for it: <quote>The performSelector: method is equivalent to sending an aSelector message directly to the receiver.</quote>Factious
thanks Josh for the clarification. You are correct; I thought performSelector/performSelector:withObject/performSelector:withObject:afterDelay all behaved the same way which was a mistake.Herwin

© 2022 - 2024 — McMap. All rights reserved.