Passing primitives through performSelectorOnMainThread
Asked Answered
D

3

10

Ok, so say i have a second thread running, but it wants to manipulate something on the main thread, like a UI item.

-(void)backgroundThread
{
    [myButton performSelectorOnMainThread:@selector(setEnabled:) withObject:(BOOL)YES waitUntilDone:YES];
     // right here, how could i pass this BOOL to the function
}

I've tried using NSNumber's numberWithBOOL, but the NSButton doesn't accept it.

Dehnel answered 25/5, 2011 at 6:51 Comment(0)
P
16

You cannot use performSelectorOnMainThread:withObject:waitUntilDone: with an argument that isn’t an Objective-C object, and you cannot use NSNumber because there’s no automatic unboxing from objects to primitive types.

One solution is to implement a similar method that accepts a button as an argument and call that method instead.

For example, in that same class:

- (void)enableButton:(NSButton *)button {
    [button setEnabled:YES];
}

and

-(void)backgroundThread{
    [self performSelectorOnMainThread:@selector(enableButton:)
                           withObject:myButton
                        waitUntilDone:YES];
}

Another solution is to implement a category on NSButton with an alternative method (e.g. -setEnabledWithNumber:), and use that method instead:

@interface NSButton (MyButtonCategory)
- (void)setEnabledWithNumber:(NSNumber *)enabled;
@end

@implementation NSButton (MyButtonCategory)
- (void)setEnabledWithNumber:(NSNumber *)enabled {
    [self setEnabled:[enabled boolValue]];
}
@end

and

-(void)backgroundThread{
    [myButton performSelectorOnMainThread:@selector(setEnabledWithNumber:)
                               withObject:[NSNumber numberWithBool:YES]
                            waitUntilDone:YES];
}
Pastelki answered 25/5, 2011 at 7:5 Comment(1)
Ya, i had already implemented a second method, but the category idea is much betterDehnel
X
3

You could use blocks:

BOOL boolValue = YES;

[self performOnMainThreadWait:YES block:^(id owner) {
    [button setEnabled:boolValue];
}];

This uses the my implementation of delayed blocks:

@implementation NSObject (HHBlockPerform)

- (void)performAfterDelay:(NSTimeInterval)delay block:(HHPerformBlock)block
{
    [self performSelector:@selector(runBlock:) withObject:[block copy] afterDelay:delay];
}

- (void)performOnMainThreadWait:(BOOL)wait block:(HHPerformBlock)block
{
    [self performSelectorOnMainThread:@selector(runBlock:)
                           withObject:[block copy]
                        waitUntilDone:wait
                                modes:[NSArray arrayWithObject:NSRunLoopCommonModes]];
}

- (void)runBlock:(HHPerformBlock)block
{
    block(self);

    [block release];
}

@end
Xerosis answered 25/5, 2011 at 8:27 Comment(3)
What exactly is a block, and what is it used for? If you could provide a link, that would be helpful.Dehnel
what is HHPerformBlock here? Is it your class or what?Morpheme
typedef void (^HHPerformBlock)(id owner);Xerosis
S
3

Yes, performSelectorOnMainThread:withObject:waitUntilDone: no longer works at all with primitive types. In the past, it kinda worked as long as you only had one argument and it was an integral type that could be losslessly converted from/to a pointer. It wasn't safe, and it wasn't pretty, but it did work.

However, Apple recently changed the implementations of these methods to retain + release their arguments, which will obviously blow up when that argument contains a BOOL or other non-object type.

Although I've created helper methods in the past, my favorite technique nowadays is to use a Higher Order Message such as the following:

[[myButton onMainThread] setEnabled:YES];

The NSInvocation used in the implementation of the HOM takes care of wrapping and unwrapping all the primitive types, and the HOM-syntax makes it easy to type and clear.

I've called this technique Little Message Dispatch.

Snaky answered 19/10, 2012 at 10:47 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.