views:

508

answers:

3

I'd like to call (void)setDoubleValue:(double)value using performSelectorOnMainThread:.

What I thought would work is:

NSNumber *progress = [NSNumber numberWithDouble:50.0];
[progressIndicator performSelectorOnMainThread:@selector(setDoubleValue:)
                                    withObject:progress
                                 waitUntilDone:NO];

Didn't work.

Then I implemented a helper method:

- (void)updateProgressIndicator:(NSNumber *)progress
{
    [progressIndicator setDoubleValue:[progress doubleValue]];
}

Works, but not really clean.

After that I tried it with NSInvocation.

NSInvocation *setDoubleInvocation;;
SEL selector = @selector(setDoubleValue:);
NSMethodSignature *signature = [[progressIndicator class]
                               instanceMethodSignatureForSelector:selector];
setDoubleInvocation = [NSInvocation invocationWithMethodSignature:signature];
[setDoubleInvocation setSelector:selector];
[setDoubleInvocation setTarget:progressIndicator];

double progress = 50.0;
[setDoubleInvocation setArgument:&progress atIndex:2];

[setDoubleInvocation performSelectorOnMainThread:@selector(invoke)
                                      withObject:nil
                                   waitUntilDone:NO];

This solution works, but it uses a lot of code and is quite slow. (Even if I store the invocation.)

Is there any other way?

+6  A: 

you'll need to write a custom un-boxing method to wrap setDoubleValue:.

- (void) setDoubleValueAsNumber: (NSNumber *) number {
   [self setDoubleValue: [number doubleValue]];
}

Ben Gottlieb
Not exactly my favorite.
Georg
But necessary. -performSelector*: doesn't do auto-boxing.
bbum
+5  A: 

If you are on Snow Leopard, you can use Blocks:

dispatch_async(dispatch_get_main_queue(), ^{
    [progressIndicator setDoubleValue: 50.0];
});
bbum
Still on a PPC here… damn. Wouldn't that operation be too resource intensive for such a small change anyway?
Georg
bbum
Any possibility that you have to wait for other operations to finish before this one can run? That could be fatal for ui updates.
Georg
The main queue is serial, thus you'll have to wait for the other items on the main queue to run. But same hold true for -performSelector: and other mechanisms.
bbum
+1  A: 

Dave Dribin has a solution for this that takes the shape of a category on NSObject. His category wraps the method call in an NSInvocation and invokes that on the main thread. This way, you can use whatever method interface you like, including primitive types for your arguments.

The Amber framework also has a category on NSObject that adds a main thread proxy, where any messages sent to that proxy are executed on the main thread.

Brad Larson