views:

435

answers:

6

Is there a more straightforward way to wait for a specific amount of time in Cocoa than what I have come up with below?

- (void) buttonPressed {
    [self makeSomeChanges];

    // give the user some visual feedback and wait a bit so he can see it
    [self displayThoseChangesToTheUser];
    [self performSelector:@selector(buttonPressedPart2:) withObject:nil afterDelay:0.35];
}

- (void) buttonPressedPart2: (id)unused {
    [self automaticallyReturnToPreviousView];
}

Just to be clear, there is no functional problem with this code -- my only beef is a stylistic one. In my case the flow is simple enough that it works, but try to encapsulate it or throw in some conditionals and things could turn ugly. It's been kind of nagging at me that I couldn't find a way to wait and then return to that same point in code like this (fictitious) example:

- (void) buttonPressed {
    [self doStuff];
    [UIMagicUnicorn waitForDuration:0.35];
    [self doStuffAfterWaiting];
}
+1  A: 

You probably want to use an NSTimer and have it send a "doStuffAfterWaiting" message as your callback. Any kind of "sleep" will block the thread until it wakes up. If it's in your U.I. thread, it'll cause your app to appear "dead." Even if that's not the case, it's bad form. The callback approach will free up the CPU to do other tasks until your specified time interval is reached.

This doc has usage examples and discusses the differences on how & where to create your timer.

Of course, performSelector:afterDelay: does the same thing.

Pestilence
A: 

Tadah!

Slightly more informative, the link points to NSThread sleepForTimeInterval:

This was actually stolen from: http://stackoverflow.com/questions/829449/thread-sleep-in-objective-c

Malaxeur
+2  A: 

There is

usleep(1000000);

and

[NSThread sleepForTimeInterval:1.0f];

both of which will sleep for 1 second.

Brad Larson
and both of which will block the thread from doing anything else. bad, if it's in the main U.I. thread.
Pestilence
True. If you need the UI to be responsive during this period, the approach suggested in the question, an NSTimer firing once (like you suggest), or a background thread performing this delay would be better.
Brad Larson
Thanks -- that's what I was wondering about, probably would have stumbled onto it had 'sleep' been one of the several synonyms for 'wait' I had thought to search on. Thanks also Pestilence for the caveats about when *not* to use these.
Purple Ninja Girl
+1  A: 

I'm not sure if it exists (yet), but with blocks in 10.6 (or PLBlocks in 10.5 and on the iPhone) it should be pretty easy to write a little wrapper like performBlock:afterDelay: that does exactly what you want without the need to sleep the entire thread. Would be a useful little piece of code indeed.

Mike Ash has written about an approach like this on his blog:

NSString *something = ...;
RunAfterDelay(0, ^{
    NSLog(@"%@", something);
    [self doWorkWithSomething: something];
});
Ole Begemann
+4  A: 

What's wrong with a simple usleep? I mean, except for the sake of "Cocoa purity", it's still much shorter than other solutions :)

FX
It blocks the thread. If it's called in the main, you're effectively killing the run loop and the app will appear "dead"
Pestilence
A: 

Here's the NSTimer way of doing it. It's might be even uglier than the method you're using, but it allows repeating events, so I prefer it.

[NSTimer scheduledTimerWithTimeInterval:0.5f 
                                 target:self
                               selector: @selector(doSomething:) 
                               userInfo:nil
                                repeats:NO];

You want to avoid something like usleep() which will just hang your app and make it feel unresponsive.

Jason Moore
I think you mean `NSTimer`, not `NSThread`.
Rob Keniger
My bad. Thanks for pointing that out. Fixed.
Jason Moore