Reputation: 3
I'm not sure how to correctly use GCD in a run loop situation where the thread might need to be stopped. The problem starts from the outset, and how or where to use CGEventCallback (which is absent from my code). The stop button won't stop the loop, and I don't think my dispatch queue is setup properly -- along with the while loop creating a huge lag.
I've read top question-answers from the search, like this and this, but one is for iOS and the other isn't relevant. Could someone show me how to properly do this?
my code:
// .h
#import <Cocoa/Cocoa.h>
@interface AppDelegate : NSObject <NSApplicationDelegate> {
IBOutlet NSTextField *textFieldBox;
IBOutlet NSButton *stop;
}
@property (assign) IBOutlet NSWindow *window;
- (void)stop;
@end
// .m
#import "AppDelegate.h"
@implementation AppDelegate
BOOL isActive = FALSE;
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
[self mainMethod];
}
- (void)mainMethod {
NSLog(@"loop started");
isActive = TRUE;
[self theArbitraryNonCompliantLoop];
NSLog(@"doing other stuff");
}
- (void)stop {
isActive = FALSE;
return;
}
- (void)theArbitraryNonCompliantLoop {
dispatch_queue_t backgroundQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_async(backgroundQueue, ^{
while (isActive) {
for (NSUInteger i = 0; i < 1000000; i++) {
[textFieldBox setStringValue:[NSString stringWithFormat:@"%lu",(unsigned long)i]];
}
}
});
}
@end
Upvotes: 0
Views: 2526
Reputation: 86651
Ignoring the name, the for loop needs to test isActive
as well. That will solve the latency issue.
The UI update needs to be done on the main thread which is easy because you can just schedule a block on the main queue to do it.
- (void)theArbitraryNonCompliantLoop {
dispatch_queue_t backgroundQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_async(backgroundQueue, ^{
while (isActive)
{
for (NSUInteger i = 0; isActive && i < 1000000; i++)
{
dispatch_async(dispatch_get_main_queue(),
^{ [textFieldBox setStringValue:[NSString stringWithFormat:@"%lu",(unsigned long)i]] };
}
}
});
}
There are still some issues here. I think, as it stands it will flood the main thread's run loop with events, so some throttling will be required. You might also consider some synchronisation for the inActive
instance variable in case the compiler optimises it by pulling it into a register at the beginning of the method. Also, it will be subject to race conditions thanks to caching etc.
Upvotes: 4
Reputation: 3646
Why would you call an arbitrary method theRunLoop
?
Either way, quoting Run Loops (Threading Programming Guide):
Both Cocoa and Core Foundation provide run loop objects to help you configure and manage your thread’s run loop. Your application does not need to create these objects explicitly; each thread, including the application’s main thread, has an associated run loop object. Only secondary threads need to run their run loop explicitly, however. The app frameworks automatically set up and run the run loop on the main thread as part of the application startup process.
Upvotes: 1
Reputation: 52546
Big mistake: You are changing a UI element on a background thread. That will cause all kinds of problems. Don't do that.
You seem to be quite confused what a runloop is. You are also trying to confuse people by calling something "theRunLoop" that just does stuff on a background thread. Your code has nothing to do with the runloop, and until you understand what a runloop is, better keep away from it.
Upvotes: 3
Reputation: 9392
My guess would be that your while loop is still on its first run. The 1000000 for loop is probably taking too long which is why it still seems like the loop is still running. To test it out put an NSLog after your for loop to see if it has exited it after you changed isActive to false.
Upvotes: 0