fix: observer leak would throw and crash the app sometimes

This commit is contained in:
louis.pontoise authored and lwouis committed 2020-03-10 14:00:37 +09:00
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)
}
}
+5 -1
View File
@@ -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()
}
+3 -1
View File
@@ -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