how to update UI controls in cocoa application from background thread
following is .m code:
#import "Th开发者_如何学运维readLabAppDelegate.h"
@interface ThreadLabAppDelegate()
- (void)processStart;
- (void)processCompleted;
@end
@implementation ThreadLabAppDelegate
@synthesize isProcessStarted;
- (void)awakeFromNib {
//Set levelindicator's maximum value
[levelIndicator setMaxValue:1000];
}
- (void)dealloc {
//Never called while debugging ????
[super dealloc];
}
- (IBAction)startProcess:(id)sender {
//Set process flag to true
self.isProcessStarted=YES;
//Start Animation
[spinIndicator startAnimation:nil];
//perform selector in background thread
[self performSelectorInBackground:@selector(processStart) withObject:nil];
}
- (IBAction)stopProcess:(id)sender {
//Stop Animation
[spinIndicator stopAnimation:nil];
//set process flag to false
self.isProcessStarted=NO;
}
- (void)processStart {
int counter = 0;
while (counter != 1000) {
NSLog(@"Counter : %d",counter);
//Sleep background thread to reduce CPU usage
[NSThread sleepForTimeInterval:0.01];
//set the level indicator value to showing progress
[levelIndicator setIntValue:counter];
//increment counter
counter++;
}
//Notify main thread for process completed
[self performSelectorOnMainThread:@selector(processCompleted) withObject:nil waitUntilDone:NO];
}
- (void)processCompleted {
//Stop Animation
[spinIndicator stopAnimation:nil];
//set process flag to false
self.isProcessStarted=NO;
}
@end
I need to clear following things as per the above code.
- How to interrupt/cancel processStart while loop from UI control?
- I also need to show the counter value in main UI, which i suppose to do with performSelectorOnMainThread and passing argument. Just want to know, is there anyother way to do that?
- When my app started it is showing 1 thread in Activity Monitor, but when i started the processStart() in background thread its creating two new thread,which makes the total 3 thread until or unless loop get finished.After completing the loop i can see 2 threads. So, my understanding is that, 2 thread created when i called performSelectorInBackground, but what about the thrid thread, from where it got created?
- What if thread counts get increases on every call of selector.How to control that or my implementation is bad for such kind of requirements?
Thanks
how to update UI controls in cocoa application from background thread
Simple: Don't.
How to interrupt/cancel processStart while loop from UI control?
Outside of processStart
, set a flag variable. Inside of processStart
, check that flag and exit the loop if it is set.
Don't try to “kill” a thread from another thread. It's always a bad idea. Tell the thread it's time to stop by setting the flag, and have the thread check that flag and stop at an appropriate time.
I also need to show the counter value in main UI, which i suppose to do with performSelectorOnMainThread and passing argument. Just want to know, is there anyother way to do that?
Yes.
When my app started it is showing 1 thread in Activity Monitor, but when i started the processStart() in background thread its creating two new thread,which makes the total 3 thread until or unless loop get finished.After completing the loop i can see 2 threads. So, my understanding is that, 2 thread created when i called performSelectorInBackground, but what about the thrid thread, from where it got created?
Profile your app using Instruments or Shark and look. It's probably the heartbeat thread for the progress indicator.
What if thread counts get increases on every call of selector.How to control that or my implementation is bad for such kind of requirements?
Every performSelectorInBackground:withObject:
message starts a thread. If your thread count isn't going down, it's because your thread method didn't exit. If your thread count is too high, it's (probably) because you started too many threads.
There is a much better way to do this.
First, the general rule in Cocoa is never sleep. Think of this as special ultra-caffeinated Cocoa. For anything you might sleep for in another framework, there is almost always a better, usually easier, way in Cocoa.
With that in mind, look at processStart
. All it does is do something every centisecond. How best to do that?
Cocoa has a class for this specific purpose: NSTimer. Create a timer that sends yourself a message at the desired interval, and respond to that message by updating the progress bar—that is, your timer callback method should essentially just be the loop body from processStart
, without the loop.
By the way, 100 updates per second is overkill. First off, the user does not care that you have made 1/5th of a pixel's worth of progress since the last time you updated the bar. Second, the screen only updates about 60 times per second anyway, so updating anything visible faster than that is pointless.
- (void)dealloc { //Never called while debugging ???? [super dealloc]; }
Assuming you put your app delegate in the MainMenu nib, the application object owns it because of that—but it doesn't know that, because it only knows about the app delegate as its delegate, which is a non-owning relationship. (And even if it were an owning relationship, that would just be two ownerships, of which the app would release one, which wouldn't help.)
However, the lifetime of the app delegate doesn't really matter. Its purpose as the delegate of the application means that it needs to last about as long as the application does, but when the application goes away, the process is exiting, which means the delegate will be deallocated as well, as part of the reclamation of the process's memory space. That's why dealloc
isn't called—the whole process space goes away at once, instead of objects being deallocated one at a time.
So, in principle, yeah, the app delegate not getting explicitly cleaned up is kind of dodgy. In practice, don't put any temporary-files clean-up in its dealloc
(use applicationWillTerminate:
instead) and you'll be fine.
I typically work around the problem by putting all my real work in one or more other objects which the app delegate owns. The app delegate creates these other controllers in applicationWillFinishLaunching:
and releases them in applicationWillTerminate:
, so those objects do get dealloc
messages. Problem solved.
精彩评论