列出 Swift 中的所有 window 个名字

List all window names in Swift

我在学习Swift。如何修复以下代码以列出 window 个名称?

import CoreGraphics

let windows = CGWindowListCopyWindowInfo(CGWindowListOption.optionAll, kCGNullWindowID)
for i in 0..<CFArrayGetCount(windows) {
  if let window = CFArrayGetValueAtIndex(windows, i) {
    print(CFDictionaryGetValue(window, kCGWindowName))
  }
}

错误:

main.swift:6:32: error: cannot convert value of type 'UnsafeRawPointer' to expected argument type 'CFDictionary?'
    print(CFDictionaryGetValue(window, kCGWindowName))
                               ^~~~~~
                                      as! CFDictionary

您可以使用 unsafeBitCast(_:to:) 将不透明的原始指针转换为 CFDictionary。请注意,您还需要将第二个参数转换为原始指针:

CFDictionaryGetValue(unsafeBitCast(window, to: CFDictionary.self), unsafeBitCast(kCGWindowName, to: UnsafeRawPointer.self))

unsafeBitCast(_:to:) 告诉编译器将该变量视为另一种类型,但是它不是很安全(因此 unsafe 前缀),建议阅读文档以获取更多详细信息,尤其是以下注释:

Warning

Calling this function breaks the guarantees of the Swift type system; use with extreme care.

在您的特定情况下,使用该函数应该没有任何问题,因为您正在使用适当的类型,正如您正在调用的 Foundation 函数的文档中声明的那样。

完整、可行的代码可能如下所示:

import CoreGraphics

let windows = CGWindowListCopyWindowInfo(CGWindowListOption.optionAll, kCGNullWindowID)
for i in 0..<CFArrayGetCount(windows) {
    let windowDict = unsafeBitCast(CFArrayGetValueAtIndex(windows, i), to: CFDictionary.self)
    let rawWindowNameKey = unsafeBitCast(kCGWindowName, to: UnsafeRawPointer.self)
    let rawWindowName = CFDictionaryGetValue(windowDict, rawWindowNameKey)
    let windowName = unsafeBitCast(rawWindowName, to: CFString?.self) as String?
    print(windowName ?? "")
}

更新 您可以通过从一开始就强制转换来更快地将 CoreFoundation 数组引入 Swift 世界:

let windows = CGWindowListCopyWindowInfo(CGWindowListOption.optionAll, kCGNullWindowID) as? [[AnyHashable: Any]]
windows?.forEach { window in
    print(window[kCGWindowName])
}

代码可读性强,但它可能会带来性能问题,因为转换为 [[AnyHashable: Any]]` 对于由大型字典组成的大型数组来说可能代价高昂。

如果您避免使用 Core Foundation 类型和方法,并尽早将值桥接到本机 Swift 类型,就会变得更容易。

这里,CGWindowListCopyWindowInfo() returns 一个 CFDictionaries 的可选 CFArray,可以桥接到相应的 Swift 类型 [[String : Any]] .然后你可以使用通常的 Swift 方法(数组枚举和字典下标)访问它的值:

if let windowInfo = CGWindowListCopyWindowInfo(.optionAll, kCGNullWindowID) as? [[ String : Any]] {
    for windowDict in windowInfo {
        if let windowName = windowDict[kCGWindowName as String] as? String {
            print(windowName)
        }
    }
}