这是我在Swift/Xcode中的第一个程序。完整的源代码和xcode项目可以在GitHub中获得。
我想复习的部分是IOKeyEventMonitor.swift。
//Copyright [2016] Jean Helou
//
//Licensed under the Apache License, Version 2.0 (the "License");
//you may not use this file except in compliance with the License.
//You may obtain a copy of the License at
//
//http://www.apache.org/licenses/LICENSE-2.0
//
//Unless required by applicable law or agreed to in writing, software
//distributed under the License is distributed on an "AS IS" BASIS,
//WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//See the License for the specific language governing permissions and
//limitations under the License.
import Foundation
import Carbon
import IOKit
import IOKit.usb
import IOKit.hid
struct IOKeyEventMonitorContext {
var lastSeenSender: String
init(lastSeenSender: String) {
self.lastSeenSender = lastSeenSender
}
}
class IOKeyEventMonitor {
private
let hidManager: IOHIDManager
let notificationCenter: CFNotificationCenter
let match: CFMutableDictionary
var lastActiveKeyboard: String = ""
var kb2is: [String: TISInputSource] = [String: TISInputSource]()
private class func createDeviceMatchingDictionary( usagePage: Int, usage: Int) -> CFMutableDictionary {
let dict = [
kIOHIDDeviceUsageKey: usage,
kIOHIDDeviceUsagePageKey: usagePage
] as NSDictionary
return dict.mutableCopy() as! NSMutableDictionary;
}
init? ( usagePage: Int, usage: Int) {
hidManager = IOHIDManagerCreate( kCFAllocatorDefault, IOOptionBits(kIOHIDOptionsTypeNone));
notificationCenter = CFNotificationCenterGetDistributedCenter();
match = IOKeyEventMonitor.createDeviceMatchingDictionary(usagePage: usagePage, usage: usage);
IOHIDManagerSetDeviceMatching( hidManager, match);
}
deinit {
// FIXME find out how to pass nil as an IOKit.IOHIDValueCallback to unregister the callback
let context = UnsafeMutableRawPointer(Unmanaged.passUnretained(self).toOpaque());
//IOHIDManagerRegisterInputValueCallback( hidManager, nil , context);
CFNotificationCenterRemoveObserver(notificationCenter, context, CFNotificationName(kTISNotifySelectedKeyboardInputSourceChanged), nil);
}
func restoreInputSource(keyboard: String) -> Void {
if let targetIs = kb2is[keyboard] {
//print("set input source to \(targetIs) for keyboard \(keyboard)");
TISSelectInputSource(targetIs)
} else {
self.storeInputSource(keyboard: keyboard);
}
}
func storeInputSource(keyboard: String) -> Void {
let currentSource: TISInputSource = TISCopyCurrentKeyboardInputSource().takeUnretainedValue();
kb2is[keyboard] = currentSource;
}
func onInputSourceChanged() -> Void {
self.storeInputSource(keyboard: self.lastActiveKeyboard);
}
func onKeyboardEvent(keyboard: String) -> Void {
if(self.lastActiveKeyboard != keyboard) {
//print("Active keyboard changed from \(self.lastActiveKeyboard) to \(keyboard)");
self.restoreInputSource(keyboard: keyboard);
self.lastActiveKeyboard = keyboard;
}
}
func start() -> Void {
let context = UnsafeMutableRawPointer(Unmanaged.passUnretained(self).toOpaque());
let myHIDKeyboardCallback: IOHIDValueCallback = {
(context, ioreturn, sender, value) in
let selfPtr = Unmanaged<IOKeyEventMonitor>.fromOpaque(context!).takeUnretainedValue();
let senderDevice = Unmanaged<IOHIDDevice>.fromOpaque(sender!).takeUnretainedValue();
let vendorId = String(describing: IOHIDDeviceGetProperty(senderDevice, kIOHIDVendorIDKey as CFString));
let productId = String(describing: IOHIDDeviceGetProperty(senderDevice, kIOHIDProductIDKey as CFString));
let product = String(describing: IOHIDDeviceGetProperty(senderDevice, kIOHIDProductKey as CFString));
let keyboard = "\(product)[\(vendorId)-\(productId)]";
selfPtr.onKeyboardEvent(keyboard: keyboard);
}
let inputSourceChanged: CFNotificationCallback = {
(center, observer, name, notif, userInfo) in
let selfPtr = Unmanaged<IOKeyEventMonitor>.fromOpaque(observer!).takeUnretainedValue();
selfPtr.onInputSourceChanged()
}
CFNotificationCenterAddObserver(notificationCenter,
context, inputSourceChanged,
kTISNotifySelectedKeyboardInputSourceChanged, nil,
CFNotificationSuspensionBehavior.deliverImmediately);
IOHIDManagerRegisterInputValueCallback( hidManager, myHIDKeyboardCallback, context);
IOHIDManagerScheduleWithRunLoop( hidManager, CFRunLoopGetMain(), CFRunLoopMode.defaultMode!.rawValue);
IOHIDManagerOpen( hidManager, IOOptionBits(kIOHIDOptionsTypeNone));
}
}你怎么把这个改写成惯用的斯威夫特?特别是如何改进start()函数?
发布于 2016-11-30 03:43:48
好吧,您编写的代码比许多已经滑动了一段时间的代码(可能包括我自己)都要好。所以,对于你的第一个节目,这是令人印象深刻的。
但是,对于我的口味来说,代码是有点麻烦的,也许还有几行空白行呢?还有,在一些地方跑得太远了.看看我是如何“包装”那些长功能婴儿车的。
另外,我发现一些挑剔的人:
括号一致性( x) (x) ( x )
功能声明间距不一致:
func x(){}
func y() {}
func z() {}把:忘在fixme里
使用;
使用-> Void
//后无空格
在初始化器上不处于闭包时使用self
在自己的行中使用private,不确定是希望所有成员都是私有的,还是只希望第一个字段。(班级第一栏)
if(self.lastActiveKeyboard != keyboard)之间没有空隙吗?对于更复杂的语句,可以保存括号:if lastActiveKeyboard != keyboard {
为方便导航,没有// MARK:。我添加了一个,这样您就可以在属性+初始化器和函数之间快速跳转,因为您已经将它们分开了:)这是很好的!如果您还没有找到Show Document Items的键盘快捷方式,那么您将在列表中看到所有的FIXME:和MARK:。
错过了一种程序风格。在声明之前使用了storeInputSource .(在restore中)注意:这不是“必需的”,但它有助于您的逻辑流程(对您自己和读者),但有时您将不得不打破这取决于您的风格。
闭包通常内联到in,与此有不一致之处。我只会把它换行,如果你通过第90栏。
不一致的闭合大括号} (有时有空行,有时没有空格)
没有注释或文档(从现在起一个月你会理解这个代码吗?)那些可能会看到它的人呢?或者你要求帮助的人?)
所以大部分都是挑剔的。对这段代码没什么特别不懂的。没有什么特别的原因,我想确保您了解guard,因为其中确实有一个if let语句,但是它是对if let的一个很好的使用。只是想确保你也知道警卫的事。
这是我对您的代码的快速重新起草,自从您问:P之后,我在start()中添加了我的个人触觉。
// Copyright [2016] Jean Helou
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import Foundation
import Carbon
import IOKit
import IOKit.usb
import IOKit.hid
struct IOKeyEventMonitorContext {
var lastSeenSender: String
init(lastSeenSender: String) {
self.lastSeenSender = lastSeenSender
}
}
class IOKeyEventMonitor {
private let hidManager: IOHIDManager
let notificationCenter: CFNotificationCenter
let match: CFMutableDictionary
var lastActiveKeyboard: String = ""
var kb2is: [String: TISInputSource] = [String: TISInputSource]()
private class func createDeviceMatchingDictionary(usagePage: Int, usage: Int) -> CFMutableDictionary {
let dict = [
kIOHIDDeviceUsageKey: usage,
kIOHIDDeviceUsagePageKey: usagePage
] as NSDictionary
return dict.mutableCopy() as! NSMutableDictionary
}
init? (usagePage: Int, usage: Int) {
hidManager = IOHIDManagerCreate(kCFAllocatorDefault, IOOptionBits(kIOHIDOptionsTypeNone))
notificationCenter = CFNotificationCenterGetDistributedCenter()
match = IOKeyEventMonitor.createDeviceMatchingDictionary(usagePage: usagePage, usage: usage)
IOHIDManagerSetDeviceMatching( hidManager, match)
}
deinit {
// FIXME: Find out how to pass nil as an IOKit.IOHIDValueCallback to unregister the callback
let context = UnsafeMutableRawPointer(Unmanaged.passUnretained(self).toOpaque())
// IOHIDManagerRegisterInputValueCallback( hidManager, nil , context)
CFNotificationCenterRemoveObserver(notificationCenter,
context,
CFNotificationName(kTISNotifySelectedKeyboardInputSourceChanged),
nil)
}
// MARK: - Funcs:
func storeInputSource(keyboard: String) {
let currentSource: TISInputSource = TISCopyCurrentKeyboardInputSource().takeUnretainedValue();
kb2is[keyboard] = currentSource
}
func restoreInputSource(keyboard: String) {
if let targetIs = kb2is[keyboard] {
// print("set input source to \(targetIs) for keyboard \(keyboard)")
TISSelectInputSource(targetIs)
} else {
storeInputSource(keyboard: keyboard)
}
}
func onInputSourceChanged() {
storeInputSource(keyboard: self.lastActiveKeyboard);
}
func onKeyboardEvent(keyboard: String) {
if lastActiveKeyboard != keyboard {
// print("Active keyboard changed from \(self.lastActiveKeyboard) to \(keyboard)")
restoreInputSource(keyboard: keyboard)
lastActiveKeyboard = keyboard
}
}
func start() {
let context = UnsafeMutableRawPointer(Unmanaged.passUnretained(self).toOpaque())
let myHIDKeyboardCallback: IOHIDValueCallback = { (context, ioreturn, sender, value) in
let
selfPtr = Unmanaged<IOKeyEventMonitor>.fromOpaque(context!).takeUnretainedValue(),
senderDevice = Unmanaged<IOHIDDevice>.fromOpaque(sender!).takeUnretainedValue(),
vendorId = String(describing: IOHIDDeviceGetProperty(senderDevice,
kIOHIDVendorIDKey as CFString)),
productId = String(describing: IOHIDDeviceGetProperty(senderDevice,
kIOHIDProductIDKey as CFString)),
product = String(describing: IOHIDDeviceGetProperty(senderDevice,
kIOHIDProductKey as CFString)),
keyboard = "\(product)[\(vendorId)-\(productId)]"
selfPtr.onKeyboardEvent(keyboard: keyboard)
}
let inputSourceChanged: CFNotificationCallback = { (center, observer, name, notif, userInfo) in
let selfPtr = Unmanaged<IOKeyEventMonitor>.fromOpaque(observer!).takeUnretainedValue()
selfPtr.onInputSourceChanged()
}
CFNotificationCenterAddObserver(notificationCenter,
context,
inputSourceChanged,
kTISNotifySelectedKeyboardInputSourceChanged,
nil,
CFNotificationSuspensionBehavior.deliverImmediately)
IOHIDManagerRegisterInputValueCallback(hidManager, myHIDKeyboardCallback, context)
IOHIDManagerScheduleWithRunLoop(hidManager, CFRunLoopGetMain(), CFRunLoopMode.defaultMode!.rawValue)
IOHIDManagerOpen(hidManager, IOOptionBits(kIOHIDOptionsTypeNone))
}
}注意:由于导入,我无法编译它,所以我可能删除了一些太多的self (可能是在闭包中),但我认为我没有.
我要做的另一件事是typealias一些长得离谱的IO /碳类型,这将有助于使事情更接近左边,并使链接具有一定的可读性。例如,
typealias CFNCGDC = CFNotificationCenterGetDistributedCenter
// Or:
typealias CFNCGetDistCenter = CFNotificationCenterGetDistributedCenter
// Or if it is a function:
extension CrazyCarbonClass {
public func CFNSShortFunc(/* all the prams */) {
CFNotificationCenterLongAsHellFunc(/* prams */)
}
}但这只是我的偏好。
https://codereview.stackexchange.com/questions/148343
复制相似问题