Reputation: 689
I'm testing with run loops in standard (created by XCode) App. My App has 2 buttons:
Start Loop
- starts runloop in some mode (see code below);Stop Loop
- change self.stop
flag to stop runloop.`
- (IBAction)stopLoop:(id)sender
{
self.stop = YES;
}
- (IBAction)startLoop:(id)sender
{
self.stop = NO;
do
{
[[NSRunLoop currentRunLoop] runMode:runLoopMode beforeDate:runLoopLimitDate];
if (self.stop)
{
break;
}
} while (YES);
}
`
where:
1. runLoopMode
is one of the predefined modes (I try each, default, event tracking, modal, connection).
2. runLoopLimitDate
[NSDate distantFuture], or [NSDate distantPast], or close feature.
3. self.stop
flag is installed in other method, which called by button.
That's all, my App hasn't any other code.
AFAIU, runloop mode is a set of event sources. So, if I run runloop in some mode, runloop will be proceed those event sources, whose are associated with this mode.
By default Cocoa runs runloop in default mode and all events are proceeds greatly. But when user press startLoop
button, my App is freezing: .
startLoop
method is never break this infinity cycle. Application doesn't send any event to me, therefore UI freezing and user can't press stopLoop
button. The same problem if I run Core Foundation counterparts.
But, when I try to receive events through NSApplication (of NSWindow) method nextEventMatchingMask:untilDate:inMode:dequeue:
and pass the same mode, I receive UI events.
- (IBAction)startLoop:(id)sender
{
self.stop = NO;
do
{
NSEvent *event = [NSApp nextEventMatchingMask:NSAnyEventMask untilDate:nil inMode:NSEventTrackingRunLoopMode dequeue:YES];
if (event == nil)
{
break;
}
[NSApp sendEvent:event];
if (self.stop)
{
break;
}
} while (YES);
}
There are question: "Why if I run default run loop mode, or some other, in this way, I can't receive events?"
Thanks for your advice.
Upvotes: 0
Views: 1372
Reputation: 2419
You are assuming that running the NSRunLoop in the NSDefaultRunMode processes user input events such as key presses or mouse clicks. I don't think that's the case.
NSApplication fetches events from the event with nextEventMatchingMask:untilDate:inMode:dequeue:
. By running the run loop like that your not really fetching any event of the event queue.
Cloud you try something like this:
- (IBAction)startLoop:(id)sender
{
do
{
NSEvent *event = [NSApp nextEventMatchingMask:NSAnyEventMask];
[NSApp sendEvent:event];
if (self.stop)
{
break;
}
} while (YES);
}
(Haven't tested this).
Upvotes: 2
Reputation: 2049
The -[NSRunLoop runUntilDate:]
method spins the runloop in NSDefaultRunLoopMode
. Since you're wanting to experiment with runloop modes, you could try the code below.
I've implemented a -myRunLoopUntilDate:runMode:
method that does what runUntilDate:
is documented to do, but allows you to specify a runloop mode.
All of this is compiled in my text editor (i.e. not compiled at all), so caveat emptor.
- (NSString *) debugLogRunLoopInfo(BOOL didRun)
{
NSLog (@"didRun? %@, runMode: %@, dateNow: %@, limitDate: %@",
didRun ? @"YES" : @"NO",
runLoopMode,
[NSDate date],
limitDate);
}
- (void) myRunLoopUntilDate:(NSDate *)limitDate runMode:(NSString *)runLoopMode
{
BOOL didRun = NO;
do {
didRun = [[NSRunLoop currentRunLoop] runMode:runLoopMode beforeDate:limitDate];
[self debugLogRunLoopInfo:didRun];
} while (didRun && ([limitDate timeIntervalSinceNow] > 0));
}
- (IBAction)startLoop:(id)sender
{
BOOL didRun = NO;
do
{
[self myRunLoopUntilDate:runLimitDate runMode:runLoopMode];
if (self.stop)
{
break;
}
} while (YES);
}
Upvotes: 0
Reputation: 2049
What happens if you substitute the following code in your app in place of the -startLoop:
method?
- (NSString *) debugLogRunLoopInfo(BOOL didRun)
{
NSLog (@"didRun? %@, runMode: %@, dateNow: %@, limitDate: %@",
didRun ? @"YES" : @"NO",
runLoopMode,
[NSDate date],
limitDate);
}
- (IBAction)startLoop:(id)sender
{
BOOL didRun = NO;
do
{
didRun = [[NSRunLoop currentRunLoop] runMode:runLoopMode beforeDate:runLoopLimitDate];
[self debugLogRunLoopInfo:didRun];
if (self.stop)
{
break;
}
} while (YES);
}
Upvotes: 0