Objective-C: Refreshing FrontmostApplication
Asked Answered
K

2

2

I wrote this little program which is supposed to print the current frontmost application twice, with a 3-second break in between.

void printFrontmostApp() {
    NSRunningApplication *frontmostApplication = [NSWorkspace sharedWorkspace].frontmostApplication;
    NSLog(@"%@",frontmostApplication);
}

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        printFrontmostApp();
        sleep(3);
        printFrontmostApp();
    }
    return 0;
}

When I ran this program, I realised that frontmostApplication is not refreshed when it is called the second time. I found the solution here. But I still have two questions:

1) I want to know why the frontmostApplication is not updated.

2) How do I force it to refresh every time I call it? I don't want to receive a notification every time the frontmost application deactivates, because it is a little inefficient for my purposes.

Edit: Just to be crystal clear, let's suppose the time now is 10:00:00. I call printFrontmostApp, and it prints "Xcode" to the console, because Xcode is the current frontmost app. Then the program sleeps for 3 seconds. At 10:00:01, I opened another app, say TextEdit. At 10:00:03, my program calls printFrontmostApp for the second time. I expect it to print "TextEdit", which is the current frontmost application. But it prints "Xcode" instead. I can't understand this behaviour.

Can someone please explain what happens at 10:00:03? The function seems to "remember" the value of frontmostApplication at 10:00:00 and retains it when it is called the second time. I thought that any memory will be released once it goes out of scope, so why is this happening?

And how do I get my program to get the frontmost app at 10:00:03? I can get the frontmost app at 10:00:00, I should be able to do the same 3 seconds later right?

Koziara answered 28/10, 2015 at 14:5 Comment(1)
sleep(3) is 3 seconds or 3 milliseconds?Libau
C
1

The documentation for -[NSWorkspace runningApplications] — not the method you're using, but related — says:

Similar to the NSRunningApplication class’s properties, this property will only change when the main run loop is run in a common mode. Instead of polling, use key-value observing to be notified of changes to this array property.

From the NSRunningApplication documentation:

Properties that vary over time are inherently race-prone. For example, a hidden app may unhide itself at any time. To ameliorate this, properties persist until the next turn of the main run loop in a common mode. For example, if you repeatedly poll an unhidden app for its hidden property without allowing the run loop to run, it will continue to return NO, even if the app hides, until the next turn of the run loop.

It's a near certainty that the same principle applies to -frontmostApplication even though the documentation doesn't say so explicitly. You will never get different results by polling without allowing the run loop to run.

Cofferdam answered 28/10, 2015 at 15:41 Comment(2)
What is a run loop? Can I manually run a run loop to trigger changes to NSRuningApplication's properties?Koziara
Here is Apple's documentation of run loops. You can run the run loop manually, although if you're writing an application there's usually no need. Note that both NSWorkspace and NSRunningApplication are part of AppKit, not Foundation, so using them as part of an application is the most natural fit, although it's not required.Cofferdam
M
1

For 1) the answer is the same described in the question you have linked: You have to observe this notification, that tells you when a new application was activated:

NSWorkspaceDidActivateApplicationNotification

About 2) You have different observers for activation and deactivation like:

NSWorkspaceDidDeactivateApplicationNotification

So your are not going to observe notifications that you are not registered, please take a look at NSWorkspace Notifications for a comprehensive list.

Otherwise, please define your question about refreshing/polling (that I think it's not a good idea anyways).

Mohammedanism answered 28/10, 2015 at 14:12 Comment(0)
C
1

The documentation for -[NSWorkspace runningApplications] — not the method you're using, but related — says:

Similar to the NSRunningApplication class’s properties, this property will only change when the main run loop is run in a common mode. Instead of polling, use key-value observing to be notified of changes to this array property.

From the NSRunningApplication documentation:

Properties that vary over time are inherently race-prone. For example, a hidden app may unhide itself at any time. To ameliorate this, properties persist until the next turn of the main run loop in a common mode. For example, if you repeatedly poll an unhidden app for its hidden property without allowing the run loop to run, it will continue to return NO, even if the app hides, until the next turn of the run loop.

It's a near certainty that the same principle applies to -frontmostApplication even though the documentation doesn't say so explicitly. You will never get different results by polling without allowing the run loop to run.

Cofferdam answered 28/10, 2015 at 15:41 Comment(2)
What is a run loop? Can I manually run a run loop to trigger changes to NSRuningApplication's properties?Koziara
Here is Apple's documentation of run loops. You can run the run loop manually, although if you're writing an application there's usually no need. Note that both NSWorkspace and NSRunningApplication are part of AppKit, not Foundation, so using them as part of an application is the most natural fit, although it's not required.Cofferdam

© 2022 - 2024 — McMap. All rights reserved.