Reputation: 59
is it possible that performSelector:withObject:afterDelay:
doesn't work in subthreads?
I'm still new to objective c and Xcode so maybe I've missed something obvious... :/ I'd really appreciate some help.
All I want to do is to show an infolabel for 3 seconds, after that it shall be hidden. In case a new info is set the thread that hides the label after 3 seconds shall be canceled. (I don't want new information hidden through old threads.)
Sourcecode:
- (void) setInfoLabel: (NSString*) labelText
{
// ... update label with text ...
infoLabel.hidden = NO;
if(appDelegate.infoThread != nil) [appDelegate.infoThread cancel]; // cancel last hide-thread, if it exists
NSThread *newThread = [[NSThread alloc] initWithTarget: self selector:@selector(setInfoLabelTimer) object: nil];// create new thread
appDelegate.infoThread = newThread; // save reference
[newThread start]; // start thread
[self performSelector:@selector(testY) withObject: nil afterDelay:1.0];
}
-(void) setInfoLabelTimer
{
NSLog(@"setInfoLabelTimer");
[self performSelector:@selector(testX) withObject: nil afterDelay:1.0];
[self performSelector:@selector(hideInfoLabel) withObject: nil afterDelay:3.0];
NSLog(@"Done?");
}
-(void) testX
{
NSLog(@"testX testX testX testX testX");
}
-(void) testY
{
NSLog(@"testY testY testY testY testY");
}
-(void) hideInfoLabel
{
NSLog(@"f hideInfoLabel");
if(!([[NSThread currentThread] isCancelled])) {
AppDelegate *appDelegate = (AppDelegate *) [[UIApplication sharedApplication] delegate];
appDelegate.infoThread = nil;
appDelegate.infoLabel.hidden = YES;
[NSThread exit];
}
}
Console-Output:
As you can see performSelector:withObject:afterDelay:
DOES work (--->"testY testY testY testY testY"), but not in the subthread (which runs (--->"setInfoLabelTimer" and"Done?"))
Does anyone know why performSelector:withObject:afterDelay
: doesn't work in subthreads? (Or what's my fault? :()
Best regards, Teapot
Upvotes: 0
Views: 1942
Reputation: 162712
There is no need for threads or GCD at all to do what you want to do.
Simply use performSelector:withObject:afterDelay:
directly on the main thread, us an animation as @Rob indicated, use dispatch_after
on the main queue, or an NSTimer
.
Upvotes: 0
Reputation: 1223
If you want to call performSelector:withObject:afterDelay on thread, this thread must has a running RunLoop. Check out the Thread Programming Guide from Apple. Here is also an example for RunLoop and NSThread.
You can add the following code in the setInfoLabelTimer:
while (!self.isCancelled)
{
[[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode
beforeDate:[NSDate distantFuture]];
}
Upvotes: 1
Reputation: 437632
As an aside, you might want to consider working with Grand Central Dispatch, GCD, instead. If you want to do something in three seconds, you can:
double delayInSeconds = 3.0;
dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC));
dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
// do stuff here, and because it's in the main queue, you can do UI stuff, too
});
I'd also refer you to Migrating Away From Threads in the Concurrency Programming Guide.
Alternatively, rather than using a GCD, you can use an animation block, in which you can designate what you want to happen in 3.0 seconds. You can also animate that transition (in my example, 0.25 seconds), so that the removal of the control is a little more graceful:
[UIView animateWithDuration:0.25
delay:3.0
options:0
animations:^{
// you can, for example, visually hide in gracefully over a 0.25 second span of time
infoLabel.alpha = 0.0;
}
completion:^(BOOL finished) {
// if you wanted to actually remove the view when the animation was done, you could do that here
[infoLabel removeFromSuperview];
}];
Upvotes: 1
Reputation: 119031
If you're running a 'sub' thread (a thread which isn't the main thread) it can run in one of 2 ways:
If the thread runs in form 1, your use of performSelector
puts an item onto a queue (or tries to at least) but it will never get handled, the thread will just terminate.
If you wanted to use performSelector
on the thread you'd need to do additional work. Or, you could push the item onto the main thread where a run loop is running.
Upvotes: 1