Hi,
I am new to Objective C and Cocoa programming and have a question which I hope can be answered in this forum. I have tried going through the Apple documentation and various Objective C references and have not been able to find the answer.
I have written a very simple program with Xcode 2.5, which recursively generates md5 checksums for all files under a given directory.
I launch the generation via a button on the GUI (generated by Interface Builder and using the standard appController).
I am trying to start my processing and then pass control back to the GUI, to allow cancelling of the current operation and other actions. Initially, when the application was single threaded, there was no way to do this - the main GUI would simply hang, waiting for the processing to finish.
So, initially, I tried creating a new thread to handle the processing, using NSThread detachNewThreadSelector. This seemed to work ok, although I was having huge problems syncing data between the 2 threads to ensure only one additional thread was started at any time. I checked a few forums about the issue and the general consensus was not to use NSThread. It was noted that most new Cocoa programmers incorrectly use NSThread, when an NSTimer would be more suited to the task.
I proceeded to write some NSTimer code to spawn off 0.5 second timer, which works fine while my main routine isn't running (at least it calls my handleTimer method, as I can see the NSLog message every 0.5 seconds). When the main routine is running, I see nothing - it is pretty CPU intensive - could it be simply that the CPU doesn't get a chance to service the timer firing. My main routine isn't class based - it is simpy a C style function with bits of Objective C in it.
I have 3 problems (and one further question):
1. How can I force NSTimer to fire when there is other processing going on. Is there a way to reduce the priority of the other function (like Unix nice?)
2. I update an NStextField in my main processing loop. This never gets updated in the main GUI until the main processing has stopped. I am trying to force updates to the 2 NStextFields an NSProgressIndicator. I thought I could force this by refreshing the main NSWindow. My method is within the main appController class and I can't work out my NSWindow instance name. I can't find a way to do this - is there a way to call refresh on "self"?
3. I still can't interact with my GUI during running of my main processing loop. I guess I need to somehow handle the "event loop" of the GUI in my handleTimer) method. I have no idea how to do this. This is very similar to question 4 of this thread:
https://forums.macrumors.com/threads/168030/
but I can't find an answer there.
4. Should I abandon NSTimer altogether and go back to using threads to do this? If so, then I might try Posix threads, as I'm having no joy with NSThread.
I know I have probably missed something really obvious but I have spent more than a day trying various ways of making this work. I have figured everything else out so far by checking the Apple class references and example code but I really can't figure this one out.
Please help!
Thanks,
Jon
An example of the code I am trying to make work:
@interface md5Controller : NSObject
{
IBOutlet NSTextField *outputTextField1;
IBOutlet NSTextField *outputTextField2;
IBOutlet NSProgressIndicator *progressBar1;
}
- (void) handleTimer: (NSTimer *) timer
- (IBAction)handleCreateMd5id)sender;
- (id) init
{
// superclass may return nil
if ( (self = [super init]) )
{
NSLog (@"md5Controller::init()");
}
NSLog (@"md5Controller::init() initialising default preference values");
radioMd5TypeValue = MD5_INTERNAL;
radioFileSystemValue = SYSTEM_UNIX;
switchLogValue = LOG_FALSE;
NSTimer *timer;
timer = [NSTimer scheduledTimerWithTimeInterval:0.5 target: self selector: @selector(handleTimer userInfo: nil repeats: YES];
return self;
}
- (void) handleTimer: (NSTimer *) timer
{
NSLog (@"md5Controller::handleTimer()");
[progressBar1 displayIfNeeded];
[outputTextField1 setNeedsDisplay:YES];
[outputTextField2 setNeedsDisplay:YES];
}
- (IBAction)handleCreateMd5id)sender
{
int result;
NSString *tempString;
NSLog (@"md5Controller::handleCreateMd5()");
tempString = [NSString stringWithString"/Users/jon/Music/"];
result = createMd5 (tempString, radioMd5TypeValue, radioFileSystemValue, &progressBar1, &outputTextField1);
}
I am new to Objective C and Cocoa programming and have a question which I hope can be answered in this forum. I have tried going through the Apple documentation and various Objective C references and have not been able to find the answer.
I have written a very simple program with Xcode 2.5, which recursively generates md5 checksums for all files under a given directory.
I launch the generation via a button on the GUI (generated by Interface Builder and using the standard appController).
I am trying to start my processing and then pass control back to the GUI, to allow cancelling of the current operation and other actions. Initially, when the application was single threaded, there was no way to do this - the main GUI would simply hang, waiting for the processing to finish.
So, initially, I tried creating a new thread to handle the processing, using NSThread detachNewThreadSelector. This seemed to work ok, although I was having huge problems syncing data between the 2 threads to ensure only one additional thread was started at any time. I checked a few forums about the issue and the general consensus was not to use NSThread. It was noted that most new Cocoa programmers incorrectly use NSThread, when an NSTimer would be more suited to the task.
I proceeded to write some NSTimer code to spawn off 0.5 second timer, which works fine while my main routine isn't running (at least it calls my handleTimer method, as I can see the NSLog message every 0.5 seconds). When the main routine is running, I see nothing - it is pretty CPU intensive - could it be simply that the CPU doesn't get a chance to service the timer firing. My main routine isn't class based - it is simpy a C style function with bits of Objective C in it.
I have 3 problems (and one further question):
1. How can I force NSTimer to fire when there is other processing going on. Is there a way to reduce the priority of the other function (like Unix nice?)
2. I update an NStextField in my main processing loop. This never gets updated in the main GUI until the main processing has stopped. I am trying to force updates to the 2 NStextFields an NSProgressIndicator. I thought I could force this by refreshing the main NSWindow. My method is within the main appController class and I can't work out my NSWindow instance name. I can't find a way to do this - is there a way to call refresh on "self"?
3. I still can't interact with my GUI during running of my main processing loop. I guess I need to somehow handle the "event loop" of the GUI in my handleTimer) method. I have no idea how to do this. This is very similar to question 4 of this thread:
https://forums.macrumors.com/threads/168030/
but I can't find an answer there.
4. Should I abandon NSTimer altogether and go back to using threads to do this? If so, then I might try Posix threads, as I'm having no joy with NSThread.
I know I have probably missed something really obvious but I have spent more than a day trying various ways of making this work. I have figured everything else out so far by checking the Apple class references and example code but I really can't figure this one out.
Please help!
Thanks,
Jon
An example of the code I am trying to make work:
@interface md5Controller : NSObject
{
IBOutlet NSTextField *outputTextField1;
IBOutlet NSTextField *outputTextField2;
IBOutlet NSProgressIndicator *progressBar1;
}
- (void) handleTimer: (NSTimer *) timer
- (IBAction)handleCreateMd5id)sender;
- (id) init
{
// superclass may return nil
if ( (self = [super init]) )
{
NSLog (@"md5Controller::init()");
}
NSLog (@"md5Controller::init() initialising default preference values");
radioMd5TypeValue = MD5_INTERNAL;
radioFileSystemValue = SYSTEM_UNIX;
switchLogValue = LOG_FALSE;
NSTimer *timer;
timer = [NSTimer scheduledTimerWithTimeInterval:0.5 target: self selector: @selector(handleTimer userInfo: nil repeats: YES];
return self;
}
- (void) handleTimer: (NSTimer *) timer
{
NSLog (@"md5Controller::handleTimer()");
[progressBar1 displayIfNeeded];
[outputTextField1 setNeedsDisplay:YES];
[outputTextField2 setNeedsDisplay:YES];
}
- (IBAction)handleCreateMd5id)sender
{
int result;
NSString *tempString;
NSLog (@"md5Controller::handleCreateMd5()");
tempString = [NSString stringWithString"/Users/jon/Music/"];
result = createMd5 (tempString, radioMd5TypeValue, radioFileSystemValue, &progressBar1, &outputTextField1);
}