fix: observer leak would throw and crash the app sometimes
This commit is contained in:
1 parent
9a8c83e39d
commit
9ca28eb2d8
3 files changed
+21
-7
No files matched your search
@@ -15,11 +15,11 @@ extension Optional {
|
||||
// add throw-on-nil method on Optional
|
||||
func orThrow() throws -> Wrapped {
|
||||
switch self {
|
||||
case .some(let value):
|
||||
return value
|
||||
case .none:
|
||||
Thread.callStackSymbols.forEach { print($0) }
|
||||
throw NSError.make(domain: "Optional", message: "Optional contained nil")
|
||||
case .some(let value):
|
||||
return value
|
||||
case .none:
|
||||
Thread.callStackSymbols.forEach { print($0) }
|
||||
throw NSError.make(domain: "Optional", message: "Optional contained nil")
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -64,3 +64,11 @@ extension Collection {
|
||||
return flatMap { ($0 as? [Any])?.joined() ?? [$0] }
|
||||
}
|
||||
}
|
||||
|
||||
// removing an objc KVO observer if there is none throws an exception
|
||||
extension NSObject {
|
||||
func safeRemoveObserver(_ observer: NSObject, _ key: String) {
|
||||
guard observationInfo != nil else { return }
|
||||
removeObserver(observer, forKeyPath: key)
|
||||
}
|
||||
}
|
||||
@@ -16,6 +16,10 @@ class Application: NSObject {
|
||||
}
|
||||
}
|
||||
|
||||
func removeObserver() {
|
||||
runningApplication.safeRemoveObserver(self, "isFinishedLaunching")
|
||||
}
|
||||
|
||||
private func addAndObserveWindows() {
|
||||
axUiElement = AXUIElementCreateApplication(runningApplication.processIdentifier)
|
||||
AXObserverCreate(runningApplication.processIdentifier, axObserverApplicationCallback, &axObserver)
|
||||
@@ -44,7 +48,7 @@ class Application: NSObject {
|
||||
|
||||
override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey: Any]?, context: UnsafeMutableRawPointer?) {
|
||||
guard let isFinishedLaunching = change![.newKey], isFinishedLaunching as! Bool else { return }
|
||||
runningApplication.removeObserver(self, forKeyPath: "isFinishedLaunching")
|
||||
removeObserver()
|
||||
addAndObserveWindows()
|
||||
}
|
||||
|
||||
|
||||
@@ -30,13 +30,15 @@ class Applications {
|
||||
var someAppsAreAlreadyTerminated = false
|
||||
for runningApp in runningApps {
|
||||
guard runningApp.bundleIdentifier != nil else { someAppsAreAlreadyTerminated = true; continue }
|
||||
guard Applications.map[runningApp.processIdentifier] != nil else { continue }
|
||||
guard let app = Applications.map[runningApp.processIdentifier] else { continue }
|
||||
var windowsToKeep = [Window]()
|
||||
for window in Windows.listRecentlyUsedFirst {
|
||||
guard window.application.runningApplication.processIdentifier != runningApp.processIdentifier else { continue }
|
||||
windowsToKeep.append(window)
|
||||
}
|
||||
Windows.listRecentlyUsedFirst = windowsToKeep
|
||||
// some apps never finish launching; the observer leaks for them without this
|
||||
app.removeObserver()
|
||||
Applications.map.removeValue(forKey: runningApp.processIdentifier)
|
||||
guard Windows.listRecentlyUsedFirst.count > 0 else { (App.shared as! App).hideUi(); return }
|
||||
// TODO: implement of more sophisticated way to decide which thumbnail gets focused on app quit
|
||||
|
||||
Reference in new issue
Block a user