ChatGPT解决这个技术问题 Extra ChatGPT

Using -performSelector: vs. just calling the method

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];

b
bbum

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.


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.
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.
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. ;)
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.
"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.
M
Motti Shneor

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...


Q
Quinn Taylor

@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.


To do that, you'd need to use an NSInvocation object.
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.
D
Daniel Yankowsky

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.


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.
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.
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).
a
avi

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."


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>
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.