Skip to content

Commit

Permalink
fix: 优化性能,修复越用越卡的问题
Browse files Browse the repository at this point in the history
关于越用越卡的原因: IMKServer会为每一个App的输入会话新实例化一个FireInputController对象,但是之前实例化的对象只有在窗口关闭或应用程序退出时才会销毁,所以对全局事件的监听不能放在FireInputController中,会因为上述原因造成多次监听,最终导致了越用越卡。

修复方案: 由于候选窗口CandidatesWindow是单例的,所以把事件的监听改为放在CandidatesWindow中应该就能解决
  • Loading branch information
qwertyyb committed Apr 5, 2023
1 parent cbefa87 commit 6b5a1fa
Show file tree
Hide file tree
Showing 5 changed files with 76 additions and 66 deletions.
10 changes: 7 additions & 3 deletions Fire/CandidatesView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ struct CandidateView: View {
}
.onTapGesture {
NotificationCenter.default.post(
name: Fire.candidateSelected,
name: CandidatesView.candidateSelected,
object: nil,
userInfo: [
"candidate": candidate,
Expand All @@ -66,6 +66,10 @@ struct CandidateView: View {
}

struct CandidatesView: View {
static let candidateSelected = Notification.Name("CandidatesView.candidateSelected")
static let nextPageBtnTapped = Notification.Name("CandidatesView.nextPageBtnTapped")
static let prevPageBtnTapped = Notification.Name("CandidatesView.prevPageBtnTapped")

var candidates: [Candidate]
var origin: String
var hasPrev: Bool = false
Expand Down Expand Up @@ -111,13 +115,13 @@ struct CandidatesView: View {
imageName: "arrowUp",
direction: direction,
disabled: !hasPrev,
eventName: Fire.prevPageBtnTapped
eventName: CandidatesView.prevPageBtnTapped
)
let arrowDown = getIndicatorIcon(
imageName: "arrowDown",
direction: direction,
disabled: !hasNext,
eventName: Fire.nextPageBtnTapped
eventName: CandidatesView.nextPageBtnTapped
)
if direction == CandidatesDirection.horizontal {
return AnyView(VStack(spacing: 0) { arrowUp; arrowDown })
Expand Down
37 changes: 35 additions & 2 deletions Fire/CandidatesWindow.swift
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ typealias CandidatesData = (list: [Candidate], hasPrev: Bool, hasNext: Bool)

class CandidatesWindow: NSWindow, NSWindowDelegate {
let hostingView = NSHostingView(rootView: CandidatesView(candidates: [], origin: ""))
var inputController: FireInputController?

func windowDidMove(_ notification: Notification) {
/* windowDidMove会先于windowDidResize调用,所以需要
Expand All @@ -39,12 +40,43 @@ class CandidatesWindow: NSWindow, NSWindowDelegate {
hostingView.rootView.origin = originalString
hostingView.rootView.hasNext = candidatesData.hasNext
hostingView.rootView.hasPrev = candidatesData.hasPrev
print("origin top left: \(topLeft)")
print("candidates: \(candidatesData)")
NSLog("origin top left: \(topLeft)")
NSLog("candidates: \(candidatesData)")
self.setFrameTopLeftPoint(topLeft)
self.orderFront(nil)
// NSApp.setActivationPolicy(.prohibited)
}

func bindEvents() {
let events: [NotificationObserver] = [
(CandidatesView.candidateSelected, { notification in
if let candidate = notification.userInfo?["candidate"] as? Candidate {
self.inputController?.insertCandidate(candidate)
}
}),
(CandidatesView.prevPageBtnTapped, { _ in self.inputController?.prevPage() }),
(CandidatesView.nextPageBtnTapped, { _ in self.inputController?.nextPage() }),
(Fire.inputModeChanged, { notification in
if notification.userInfo?["val"] as? InputMode == InputMode.enUS {
self.inputController?.insertOriginText()
}
})
]
events.forEach { (observer) in NotificationCenter.default.addObserver(
forName: observer.name, object: nil, queue: nil, using: observer.callback
)}
/**
* 1. 由于使用recognizedEvents在一些场景下不能监听到flagChanged事件,比如保存文件场景
* 所以这里需要使用NSEvent.addGlobalMonitorForEvents监听shift键被按下
*/
NSEvent.addGlobalMonitorForEvents(matching: .flagsChanged) { (event) in
NSLog("[CandidatesWindow] globalMonitorForEvents flagsChanged: \(event)")
if !InputSource.shared.isSelected() {
return
}
_ = self.inputController?.flagChangedHandler(event: event)
}
}

override init(
contentRect: NSRect,
Expand All @@ -60,6 +92,7 @@ class CandidatesWindow: NSWindow, NSWindowDelegate {
backgroundColor = NSColor.clear
delegate = self
setSizePolicy()
bindEvents()
}

private func limitFrameInScreen() {
Expand Down
6 changes: 0 additions & 6 deletions Fire/Fire.swift
Original file line number Diff line number Diff line change
Expand Up @@ -27,12 +27,6 @@ extension UserDefaults {
internal let SQLITE_TRANSIENT = unsafeBitCast(-1, to: sqlite3_destructor_type.self)

class Fire: NSObject {
// SwiftUI 界面事件
static let candidateSelected = Notification.Name("Fire.candidateSelected")
static let candidateListUpdated = Notification.Name("Fire.candidateListUpdated")
static let nextPageBtnTapped = Notification.Name("Fire.nextPageBtnTapped")
static let prevPageBtnTapped = Notification.Name("Fire.prevPageBtnTapped")

// 逻辑
static let candidateInserted = Notification.Name("Fire.candidateInserted")
static let inputModeChanged = Notification.Name("Fire.inputModeChanged")
Expand Down
47 changes: 29 additions & 18 deletions Fire/FireInputController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,21 @@ class FireInputController: IMKInputController {
monitorList: []
)

override init() {
super.init()
NSLog("[FireInputController] init without params")
}

override init!(server: IMKServer!, delegate: Any!, client inputClient: Any!) {
super.init(server: server, delegate: delegate, client: inputClient)
NSLog("[FireInputController] init with params")
}

deinit {
NSLog("[FireInputController] deinit")
clean()
}

private var _originalString = "" {
didSet {
if self.curPage != 1 {
Expand All @@ -55,6 +70,12 @@ class FireInputController: IMKInputController {
}
}
}
func prevPage() {
self.curPage = self.curPage > 1 ? self.curPage - 1 : 1
}
func nextPage() {
self.curPage = self._hasNext ? self.curPage + 1 : self.curPage
}

private func markText() {
let attrs = mark(forStyle: kTSMHiliteConvertedText, at: NSRange(location: NSNotFound, length: 0))
Expand Down Expand Up @@ -91,7 +112,7 @@ class FireInputController: IMKInputController {
return nil
}

private func flagChangedHandler(event: NSEvent) -> Bool? {
func flagChangedHandler(event: NSEvent) -> Bool? {
if Defaults[.disableEnMode] {
return nil
}
Expand Down Expand Up @@ -330,6 +351,13 @@ class FireInputController: IMKInputController {
try client()?.insertText(value, replacementRange: replacementRange())
clean()
}

// 往输入框中插入原始字符
func insertOriginText() {
if self._originalString.count > 0 {
self.insertText(self._originalString)
}
}

// 获取当前输入的光标位置
private func getOriginPoint() -> NSPoint {
Expand All @@ -346,21 +374,4 @@ class FireInputController: IMKInputController {
curPage = 1
CandidatesWindow.shared.close()
}

func notificationList() -> [NotificationObserver] {
return [
(Fire.candidateSelected, { notification in
if let candidate = notification.userInfo?["candidate"] as? Candidate {
self.insertCandidate(candidate)
}
}),
(Fire.prevPageBtnTapped, { _ in self.curPage = self.curPage > 1 ? self.curPage - 1 : 1 }),
(Fire.nextPageBtnTapped, { _ in self.curPage = self._hasNext ? self.curPage + 1 : self.curPage }),
(Fire.inputModeChanged, { notification in
if self._originalString.count > 0, notification.userInfo?["val"] as? InputMode == InputMode.enUS {
self.insertText(self._originalString)
}
})
]
}
}
42 changes: 5 additions & 37 deletions Fire/FireInputServer.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@
import Foundation
import Defaults

private var previousClientIdentifier: String = ""
private var inputModeCache: [String: InputMode] = [:]

extension FireInputController {
Expand All @@ -32,60 +31,29 @@ extension FireInputController {
}

private func savePreviousClientInputMode() {
if previousClientIdentifier.count > 0 {
if let identifier = CandidatesWindow.shared.inputController?.client()?.bundleIdentifier() {
// 缓存当前输入模式
inputModeCache.updateValue(inputMode, forKey: previousClientIdentifier)
inputModeCache.updateValue(inputMode, forKey: identifier)
}
}

func clearEventListener() {
temp.monitorList.forEach { (monitor) in
if let m = monitor {
NSEvent.removeMonitor(m)
}
}
temp.observerList.forEach { (observer) in
NotificationCenter.default.removeObserver(observer)
}
temp.monitorList = []
temp.observerList = []
}
func previousClientHandler() {
clean()
clearEventListener()
savePreviousClientInputMode()
}

/**
* 1. 由于使用recognizedEvents在一些场景下不能监听到flagChanged事件,比如保存文件场景
* 所以这里需要使用NSEvent.addGlobalMonitorForEvents监听shift键被按下
* 2. 当client变化时,deactiveServer 和 activeServer 的执行是不固定的,有可能 activeServer 先执行,所以需要在activeServer中执行清理逻辑
*/
override func activateServer(_ sender: Any!) {
NSLog("[FireInputController] activate server: \(client()?.bundleIdentifier() ?? sender.debugDescription)")

previousClientHandler()

if let identifier = client()?.bundleIdentifier() {
previousClientIdentifier = identifier
}

// 监听candidateView点击,翻页事件
notificationList().forEach { (observer) in temp.observerList.append(NotificationCenter.default.addObserver(
forName: observer.name, object: nil, queue: nil, using: observer.callback
))}
CandidatesWindow.shared.inputController = self

if Defaults[.disableEnMode] {
Fire.shared.toggleInputMode(.zhhans)
return
return
}

activeCurrentClientInputMode()
temp.monitorList.append(NSEvent.addGlobalMonitorForEvents(matching: .flagsChanged) { (event) in
if !InputSource.shared.isSelected() {
return self.clearEventListener()
}
_ = self.handle(event, client: self.client())
})
}
override func deactivateServer(_ sender: Any!) {
NSLog("[FireInputController] deactivate server: \(client()?.bundleIdentifier() ?? "no client deactivate")")
Expand Down

0 comments on commit 6b5a1fa

Please sign in to comment.