gpt4 book ai didi

线程中的 Swift 3 CFRunLoopRun?

转载 作者:行者123 更新时间:2023-11-28 16:03:07 27 4
gpt4 key购买 nike

我刚刚制作了一个简单的测试应用程序来显示击键的键码和修饰符。它适用于 3 次击键,然后应用程序崩溃。当它崩溃时,调试控制台最后只显示 (LLDB) 。任何建议可能是什么原因造成的?也许与线程或指针有关,但我不确定如何解决这个问题。我包括下面的代码。我真的很感激任何帮助!谢谢!

import Cocoa
import Foundation

class ViewController: NSViewController {

@IBOutlet weak var textField: NSTextFieldCell!
let speech:NSSpeechSynthesizer = NSSpeechSynthesizer()

func update(msg:String) {
textField.stringValue = msg
print(msg)
speech.startSpeaking(msg)
}

func bridgeRetained<T : AnyObject>(obj : T) -> UnsafeRawPointer {
return UnsafeRawPointer(Unmanaged.passRetained(obj).toOpaque())
}

override func viewDidLoad() {
super.viewDidLoad()
DispatchQueue.global().async {
func myCGEventCallback(proxy: CGEventTapProxy, type: CGEventType, event: CGEvent, refcon: UnsafeMutableRawPointer?) -> Unmanaged<CGEvent>? {

let parent:ViewController = Unmanaged<ViewController>.fromOpaque(refcon!).takeRetainedValue()

if [.keyDown].contains(type) {
let flags:CGEventFlags = event.flags
let pressed = Modifiers(rawValue:flags.rawValue)
var msg = ""

if pressed.contains(Modifiers(rawValue:CGEventFlags.maskAlphaShift.rawValue)) {
msg+="caps+"
}
if pressed.contains(Modifiers(rawValue:CGEventFlags.maskShift.rawValue)) {
msg+="shift+"
}
if pressed.contains(Modifiers(rawValue:CGEventFlags.maskControl.rawValue)) {
msg+="control+"
}
if pressed.contains(Modifiers(rawValue:CGEventFlags.maskAlternate.rawValue)) {
msg+="option+"
}
if pressed.contains(Modifiers(rawValue:CGEventFlags.maskCommand.rawValue)) {
msg += "command+"
}
if pressed.contains(Modifiers(rawValue:CGEventFlags.maskSecondaryFn.rawValue)) {
msg += "function+"
}

var keyCode = event.getIntegerValueField(.keyboardEventKeycode)
msg+="\(keyCode)"

DispatchQueue.main.async {
parent.update(msg:msg)
}

if keyCode == 0 {
keyCode = 6
} else if keyCode == 6 {
keyCode = 0
}

event.setIntegerValueField(.keyboardEventKeycode, value: keyCode)
}
return Unmanaged.passRetained(event)
}

let eventMask = (1 << CGEventType.keyDown.rawValue) | (1 << CGEventType.keyUp.rawValue)

guard let eventTap = CGEvent.tapCreate(tap: .cgSessionEventTap, place: .headInsertEventTap, options: .defaultTap, eventsOfInterest: CGEventMask(eventMask), callback: myCGEventCallback, userInfo: UnsafeMutableRawPointer(mutating: self.bridgeRetained(obj: self))) else {
print("failed to create event tap")
exit(1)
}
let runLoopSource = CFMachPortCreateRunLoopSource(kCFAllocatorDefault, eventTap, 0)
CFRunLoopAddSource(CFRunLoopGetCurrent(), runLoopSource, .commonModes)
CGEvent.tapEnable(tap: eventTap, enable: true)
CFRunLoopRun()
}
// Do any additional setup after loading the view.
}

override var representedObject: Any? {
didSet {
// Update the view, if already loaded.
}
}

}

最佳答案

主要问题是引用计数:您创建了一个保留在安装事件处理程序时引用 View Controller ,这只发生一次。然后你在回调中使用一个引用,这发生在每个点击事件。因此,引用计数最终会降为零,并且 View Controller 被释放,导致崩溃。

最好将未保留的引用传递给回调,并注意当 View Controller 被释放时,事件处理程序被卸载。

此外,无需为 OS X 应用程序创建单独的运行循环,或异步分派(dispatch)处理程序创建。

使回调成为一个全局函数,而不是一个方法。使用takeUnretainedValue() 获取 View Controller 引用:

func myCGEventCallback(proxy: CGEventTapProxy, type: CGEventType, event: CGEvent, refcon: UnsafeMutableRawPointer?) -> Unmanaged<CGEvent>? {

let viewController = Unmanaged<ViewController>.fromOpaque(refcon!).takeUnretainedValue()
if type == .keyDown {

var keyCode = event.getIntegerValueField(.keyboardEventKeycode)
let msg = "\(keyCode)"

DispatchQueue.main.async {
viewController.update(msg:msg)
}

if keyCode == 0 {
keyCode = 6
} else if keyCode == 6 {
keyCode = 0
}
event.setIntegerValueField(.keyboardEventKeycode, value: keyCode)
}
return Unmanaged.passRetained(event)
}

在 View Controller 中,保留对运行循环源的引用这样你就可以在 deinit 中删除它,然后使用passUnretained() 将指向 View Controller 的指针传递给回调:

class ViewController: NSViewController {

var eventSource: CFRunLoopSource?

override func viewDidLoad() {
super.viewDidLoad()

let eventMask = (1 << CGEventType.keyDown.rawValue) | (1 << CGEventType.keyUp.rawValue)
let userInfo = UnsafeMutableRawPointer(Unmanaged.passUnretained(self).toOpaque())

if let eventTap = CGEvent.tapCreate(tap: .cgSessionEventTap, place: .headInsertEventTap,
options: .defaultTap, eventsOfInterest: CGEventMask(eventMask),
callback: myCGEventCallback, userInfo: userInfo) {
self.eventSource = CFMachPortCreateRunLoopSource(kCFAllocatorDefault, eventTap, 0)
CFRunLoopAddSource(CFRunLoopGetCurrent(), self.eventSource, .commonModes)
} else {
print("Could not create event tap")
}
}

deinit {
if let eventSource = self.eventSource {
CFRunLoopRemoveSource(CFRunLoopGetCurrent(), eventSource, .commonModes)
}
}

// ...

}

另一种选择是安装/卸载事件处理程序viewDidAppearviewDidDisappear

关于线程中的 Swift 3 CFRunLoopRun?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/40706095/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com