I'm trying to create a UILabel which will inform the user of what is going on while he waits. However the UILabel always delay its text update until after the system goes idle again.
The process:
[infoLine performSelectorOnMainThread:#selector(setText:) withObject:#"Calculating..." waitUntilDone:YES];
[distanceManager calc]; // Parses a XML and does some calculations
[infoLine performSelectorOnMainThread:#selector(setText:) withObject:#"Idle" waitUntilDone:YES];
Should not waitUntilDone make this happen "immediately"?
If you are doing this on the main UI thread, don't use waitUntilDone. Do a setText, setNeedsDisplay on the full view, set a NSTimer to launch what you want to do next starting 1 millisecond later, then return from your function/method. You may have to split your calculation up into chucks that can be called separately by the timer, maybe a state machine with a switch statement (select chunk, execute chunk, increment chunk index, exit) that gets called by the timer until it's done. The UI will jump in between your calculation chunks and update things. So make sure your chunks are fairly short (I use 15 to 200 milliseconds).
Yes waitUntilDone makes the setText: happen immediately, but setting the label's text does not mean the screen is updated immediately.
You may need to call -setNeedsDisplay or even let the main run loop tick once before the screen can be updated.
Here's a useful function I added to a subclass of UIViewController. It performs the selector in the next run loop. It works, but do you think I should make NSTimer *timer an instance variable since it's likely this method will be called more than once.
- (void)scheduleInNextRunloopSelector:(SEL)selector {
NSDate *fireDate = [[NSDate alloc] initWithTimeIntervalSinceNow:0.001]; // 1 ms
NSTimer *timer = [[NSTimer alloc]
initWithFireDate:fireDate interval:0.0 target:self
selector:selector userInfo:nil repeats:NO];
[fireDate release];
[[NSRunLoop currentRunLoop] addTimer:timer forMode:NSDefaultRunLoopMode];
[timer release];
}
Use performSelector:(SEL) withObject:(id) afterDelay:(NSTimeInterval):
self.infoLine.text = #"Calculating...";
[self performSelector:#selector(likeABoss) withObject:nil afterDelay:0.001];
//...
-(void) likeABoss {
// hard work here
self.infoLine.text = #"Idle";
}
Related
I'm kind of new to multithreading, and need some advice.
I'm using ARC in my code.
Problem : I've set up NSTimer in my app to fire every 1 second some method which creates and starts thread like this
//Create a new thread
mSomeThread = [[NSThread alloc] initWithTarget:self selector:#selector(someMethod) object:nil];
//start the thread
[mSomeThread start];
Where mSomeThread is an ivar
Let say the execution of mSomeThread takes more than 1 second, and the mSomeThread is allocated second time, i.e. according to ARC "rules" its released before be allocated one more time.
Does it mean that the first thread doesn't complete and and is forced to quite ?
An NSThread retains itself for the duration of its execution. There's no risk that resetting mSomeThread will cause a running thread to be terminated prematurely.
Yes. If you really need to keep reference to the current thread of execution for your someMethod then you need to wait for it to complete before you can actually start a new thread.
A quick way of doing this would be to add
while ([mSomeThread isExecuting]) {
sleep(1);
}
immediately after [mSomeThread start];.
By the way I'd rather re-implement NSThread and setup a repetitive NSTimer inside its main implementation.
Something like:
- main {
#autoreleasepool {
[NSTimer scheduledTimerWithTimeInterval:1 target:self selector:#selector(someMethod) userInfo:nil repeats:NO];
[[NSRunLoop currentRunLoop] run];
}
}
I am using MBProgressHUD to display a progress indicator. The delegate that is called when the indicator is shown is:
- (void)myTask {
while (self.show_progress == NO){
}
}
basically when it goes out of the loop it dismisses the indicator. Now the issue is that I would like do something more in this method. I would like to check for how long has the indicator been spinning for, if it has been more than 5 seconds then I would like to re-load the request. The question is how do I check for this?
This is just to prevent the apps waiting for an infinite amount of time just in case the response never got back or got stuck somewhere.
I'm not familiar with MBProgressHUD , but on general terms you could do the following:
When you first make the request do:
NSDate *startTime = [NSDate date];
Then whenever you want to check how long has it been:
NSTimeInterval timePassed = -[startTime timeIntervalSinceNow];
timePassed will have the value, in seconds, of how long has it been since you made your request. May be you should consider using NSTimer for this: Schedule a timer that will fire 5 seconds after you performed your request, if it triggers cancel the request but if you receive a response before the timer triggers invalidate the timer.
If I understand correctly, your code just waits until the property show_progress becomes NO. I don't know why your code does this, it seems a little inelegant. If you want to keep it this way, at least use a condition lock to prevent the 100% CPU usage:
Prepare the condition lock like this:
NSConditionLock *progressLock = [[NSConditionLock alloc] initWithCondition:0];
In your second thread, once your loading stuff or whatever finishes, change the condition like this:
[progressLock lock];
[progressLock unlockWithCondition:1];
And in your method, do this:
- (void)myTask {
NSDate *timeoutDate = [NSDate dateWithTimeIntervalSinceNow:5];
if ([progressLock lockWhenCondition: 1 beforeDate:timeoutDate) {
// we aquired the lock, processing has finished
[progressLock unlock];
} else {
// we didn't aquire the lock because the 5 seconds have passed
// reload the request or do whatever you want to do
}
}
This method waits 5 seconds, and then times out. It uses no CPU in those 5 seconds, because it waits for a signal at the lockWhenCondition:beforeDate: call.
The way I've gone about similar situations is to set up a timer. The basic concept would be to start the timer when the indicator starts spinning. Then invalidate it when the indicator stops. Else if it goes on for 5 seconds, execute your method.
So in your header, you'll want
NSTimer *myTimer;
then in the implementation, when you start the indicator spinning,
[indicator startAnimating];
if (myTimer != nil) {
[myTimer invalidate];
myTimer = nil;
}
myTimer = [NSTimer scheduledTimerWithTimeInterval:5.0 target:self selector:#selector(reloadRequest) serInfo:nil repeats:NO];
when you stop the indicator from spinning, send [myTimer invalidate]; and myTimer = nil;. If the specified time is reached beforehand, reload the request in your reloadRequest method
I make a Voca App.
After playing voice file and showing English text, it shows the meaning label.
So, I want to hide label texts before playing next voice file.
I have problem.df
These labels are not hidden.
Approaching the label at timer, it can's renewal string of label.
What is the problem?
Help me, please!
- (void)showLabel{
Word *word = [wordArray objectAtIndex:selectedIndex];
[simpleMeaning setText:word.mean];
NSTimer *timer2 = [[NSTimer scheduledTimerWithTimeInterval:2 target:self selector:#selector(hideLabel) userInfo:nil repeats:NO] retain];
[timerArray addObject:timer2];
[timer2 release];
timer2 = nil;
}
- (void)hideLabel{
[simpleMeanig setText:#" "];
++selectedIndex;
[self filePlay];
}
I'm wondering if the call to filePlay is blocking the UI update (hard to tell without seeing the guts of filePlay).
This is just a guess, but instead of:
[self filePlay];
try:
[self performSelectorOnMainThread:(#selector(filePlay) withObject:nil waitUntilDone:NO];
This will defer the call to filePlay until after the current event (as a result of the timer firing) has been processed (which should in turn should allow the UI to refresh itself).
I aims to release a movieplayer (theMovie) and then start another action (so-called playButtonClicked) after it is completely released. I used performSelector to delay the "playButtonClicked" for 1 second and it works well. The code is:
[theMovie release];
[self performSelector:#selector(playButtonClicked) withObject:nil afterDelay:1];
However, I don't want to always delay 1 second. I want to start the "playButtonClicked" as soon as "theMovie" is completely released. I tried the following code, but it didn't work because [timer userInfo] never is nil.
Does anybody know how to check a movieplayer release finished.
[theMovie release];
//[self performSelector:#selector(playButtonClicked) withObject:nil afterDelay:1];
NSTimer *atimer = [NSTimer scheduledTimerWithTimeInterval:0.1 target:self
selector:#selector(waitForReleaseFinish:)
userInfo: (MPMoviePlayerController*) theMovie repeats:YES];
The code of waitForRleaseFinish: (NSTimer *)timer is:
if ([timer userInfo]==nil) //here I actually want to test if (theMovie==nil),but I don't know how to do and I'm not sure if it is a correct way to determine the release finished.
{
[timer invalidate];
[self playButtonClicked];
}
Look forward to helps. Thank you.
There is no need.
If you just release the player and then call playButtonClicked, like this:
[theMovie release];
[self playButtonClicked];
It won't execute the second line until the first is completed, or until theMovie is released. This is all on the same thread so it will execute in order. You don't need a timer for this. Although in situations where what you're waiting for to finish executes on a new thread, you would use a callback, rather than guessing how long it takes (which is much less than 1 second!).
Also, just so you don't misunderstand, "completely releasing" is just subtracting the retainCount by one. It will automatically deallocate when it reaches zero.
Just as a side note, why is it important that theMovie is released (deallocated?) before playButtonClicked is executed?
Also, your waitForReleaseFinish: code would work, but it's unnecessary because theMovie would be released before the timer is created.
I have a NSThread that i would like to timeout after a certain amount of time.
[NSThread detachNewThreadSelector:#selector(someFuntion) toTarget:self withObject:nil];
- (void) someFunction {
//Some calculation that might take a long time.
//if it takes more then 10 seconds i want it to end and display a error message
}
Any help you can provide on this would be greatly appreciated.
Thanks,
Zen_silence
Instead of using NSThread, use NSOperation. You could then keep a reference to the operation, and set a NSTimer for 10 seconds. If the timer fires, tell the operation to cancel itself.
Try something like this:
workerThread = [[NSThread alloc] initWithTarget:self selector:#selector(someFunction) object:nil];
[workerThread start];
timeoutTimer = [NSTimer scheduledTimerWithTimeInterval:10.0 target:workerThread selector:#selector(cancel) userInfo:nil repeats:NO];
Be sure that you (1) check workerThread.isCancelled when the thread finishes to see whether the thread timed out, and (2) call [timoutTimer invalidate] to clean up the timer if the thread did not time out.