Reputation: 2859
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.
Upvotes: 10
Views: 8107
Reputation: 99
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.
Upvotes: 3
Reputation: 3198
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
Upvotes: 3
Reputation:
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];
}
Upvotes: 16